Started: 2020-09-02
Last edited: 2021-01-24 13:25:29

### packages ==================================================================
library(tidyverse)

# single cell
library(Seurat)

# rmd
library(kableExtra)

# plotting
library(patchwork)
library(ggthemes)

# go enrichment
library(clusterProfiler)
library(org.Hs.eg.db)

We can take a first pass at annotating the clusters from our 10x data by comparing them to the reference dataset that we put together. This was done by compiling celltype-averaged expression values from the following single cell RNA-seq studies: (Pavličev et al. 2017; Vento-Tormo et al. 2018; Suryawanshi et al. 2018).

1 Data

1.1 Samples

This is the sample information sent by Alice. Not sure about the INP id for AL09 and AL10. The library for INP188 villi (associated with AL07) was problematic, so is not used.

sample.info <- read_csv("../ext/from-alice/sample_info.csv")

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  AL_id = col_character(),
  inp_id = col_character(),
  sample_id = col_character(),
  covid = col_character(),
  tissue = col_character()
)
sample.info %>% kable(caption = "Sample information") %>% kable_styling(full_width = FALSE)
Sample information
AL_id inp_id sample_id covid tissue
AL01 INP88 COVID_4 covid decidua
AL02 INP88 COVID_4 covid villi
AL03 INP90 COVID_5 covid decidua
AL04 INP90 COVID_5 covid villi
AL05 INP187 CNTRL_1 cntrl decidua
AL06 INP187 CNTRL_1 cntrl villi
AL07 INP188 CNTRL_2 cntrl decidua
AL08 INP188 CNTRL_2 cntrl villi
AL09 NA CNTRL_3 cntrl decidua
AL10 NA CNTRL_3 cntrl villi

1.2 Seurat-processed data

Read the Seurat-processed (by Eric) data sent by Alice.

seur <- readRDS("../ext/from-alice/placenta.rds")

head(seur@meta.data)

1.2.1 Add sample metadata

The orig.ident column in the metadata represents the sample IDs from the sample info table above. We just have rename them to left-pad the numbers for consistency, and set new cluster names as default idents.

# left pad orig.idents
seur@meta.data$orig.ident <- gsub("(AL)(\\d)$", "\\10\\2", seur@meta.data$orig.ident)

# add additional metadata (covid status, tissue)
seur@meta.data$covid <- sample.info$covid[match(x = seur@meta.data$orig.ident, 
                                                table = sample.info$AL_id)]
seur@meta.data$tissue <- sample.info$tissue[match(x = seur@meta.data$orig.ident,
                                                  table = sample.info$AL_id)]

# rename clusters for consistency
seur$seurat_clusters <- paste0("clust_", seur$seurat_clusters)
seur$seurat_clusters <- gsub("(clust_)(\\d$)", "\\10\\2", as.character(seur$seurat_clusters)) # left pad
seur$seurat_clusters <- factor(seur$seurat_clusters, levels = paste0("clust_", c("00", "01", "02", "03", "04", "05", "06", "07", "08", "09", 10:34)))

# set seurat clusters as default idents
Idents(seur) <- seur@meta.data$seurat_clusters
# add sample_id
seur@meta.data$sample_id <- sample.info$sample_id[match(x = seur@meta.data$orig.ident, 
                                                        table = sample.info$AL_id)]

1.3 Average by cluster

We have to get transcriptomes averaged by clusters, so that we can compare them with the reference data in order to narrow down annotations. For averaging, we will use sctransform normalized data since this is what we use for marker gene detection down the line.

# average sctransformed expression
seur.avg <- AverageExpression(seur)

# averaged data to use for correlations (SCT transformed)
plac <- seur.avg$SCT

1.4 Reference data

# read reference datasets
ref <- read.csv("../results/01_reference-atlas/vento-surya-pavli_joined.csv", stringsAsFactors = FALSE)

1.5 Get data in shape

We will join the data with the reference data so they are both in the same dataframe.

# add gene names column
plac$gene_name <- rownames(plac)

# inner join
plac <- dplyr::inner_join(plac, ref, by = "gene_name")

head(plac)

2 Correlations within the data

Before moving to annotating clusters by comparison with the reference dataset, first we will look at the correlation structure within the dataset.

2.1 Correlation matrix

# build correlation matrix from expression data
cor.matrix <- cor(plac %>% dplyr::select(starts_with("clust")), method = 'spearman')

# reorder correlation matrix based on clustering
dd <- as.dist((1 - cor.matrix)) 
hc <- hclust(dd, method = 'complete')
cormat <- cor.matrix[hc$order, hc$order]

# melt correlation matrix
dat <- reshape2::melt(cormat, na.rm = T)

## plot
p <- ggplot(data = dat, aes(Var1, Var2, fill = value)) +
  geom_tile(colour = "white") +
  scale_fill_gradient(low = 'white', high = 'red',
                      name = "Spearman\nCorrelation") +
  coord_fixed(ratio = 1) +
  labs(title = "Correlations among clusters") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, size = 8, 
                                   hjust=1, vjust = 0.5),
        axis.text.y = element_text(size=8),
        axis.ticks.length = unit(0.15, units = c('lines')),
        legend.title = element_text(size = 10),
        axis.title = element_blank(),
        panel.grid = element_blank(),
        panel.border = element_blank(),
        legend.key.size = unit(0.8, units = c('lines')))

ggsave(p, filename = "../results/02_annotation/plots/corrplot_clusters.pdf", 
       device = "pdf", width = 7, height = 6, units = "in")

print(p)

2.2 Heirarchical clustering

# build correlation matrix from expression data
cor.matrix <- cor(plac %>% dplyr::select(starts_with("clust")), method = 'spearman')

# reorder correlation matrix based on clustering
dd <- as.dist((1 - cor.matrix)) 
hc <- hclust(dd, method = 'complete')

pdf(file = "../results/02_annotation/plots/hclust_clusters.pdf", width = 7, height = 5)
plot(hc, cex = 0.8)
dev.off()
null device 
          1 
plot(hc, cex = 0.8)

There are three big cluster blocks, with substructure within them. The cluster blocks also correspond well with the UMAP plot that was sent by Alice.

3 Annotation by correlation

Let’s now actually measure correlations between all clusters with the reference datasets. We will treat our data as query dataset and for each cluster assign top 3 cell types in each reference dataset.

refdata <- c("vento", "surya", "pavli")

# build correlation matrix from expression data
cor.matrix <- cor(plac[, names(plac)[names(plac) != "gene_name"]], 
                  method = 'spearman')

# reorder correlation matrix based on clustering
dd <- as.dist((1 - cor.matrix))
hc <- hclust(dd, method = 'complete')
cor.matrix <- cor.matrix[hc$order, hc$order]

# melt correlation matrix
cormat <- reshape2::melt(cor.matrix, na.rm = T)
cormat$Var1_source <- sapply(strsplit(as.character(cormat$Var1), split = "_"), "[[", 1)
cormat$Var2_source <- sapply(strsplit(as.character(cormat$Var2), split = "_"), "[[", 1)

# subset
cormat <- cormat[which(cormat$Var1_source %in% refdata & 
                   cormat$Var2_source == "clust"), ]

# top 3 matches with highest correlation coefficients
topmatch <- data.frame( # empty df to fill top matches from the loop below
  cluster = unique(as.character(cormat$Var2)) %>% sort(),
  vento.top1 = NA,
  surya.top1 = NA,
  pavli.top1 = NA,
  vento.top2 = NA,
  surya.top2 = NA,
  pavli.top2 = NA,
  vento.top3 = NA,
  surya.top3 = NA,
  pavli.top3 = NA
)

cormat$top3 <- NA

for(i in unique(cormat$Var2)){
  for(j in refdata){
    # identify top3 match indices
    ind <- which(cormat$Var2 == i & cormat$Var1_source == j)
    val <- cormat$value[ind]
    top3ind <- ind[order(val, decreasing = TRUE)[1:3]]
    
    # assign match ranking to correlation data for plotting
    cormat$top3[top3ind[1]] <- "1"
    cormat$top3[top3ind[2]] <- "2"
    cormat$top3[top3ind[3]] <- "3"
    
    # assign top matches to topmatches dataframe
    topmatch[topmatch$cluster == i, paste0(j, ".top1")] <- gsub(paste0(j, "_"), "", as.character(cormat$Var1[top3ind[1]]))
    topmatch[topmatch$cluster == i, paste0(j, ".top2")] <- gsub(paste0(j, "_"), "", as.character(cormat$Var1[top3ind[2]]))
    topmatch[topmatch$cluster == i, paste0(j, ".top3")] <- gsub(paste0(j, "_"), "", as.character(cormat$Var1[top3ind[3]]))
  }
}

3.1 Plots

# plotting function
clustAnnoPlot <- function(dat, query, reference) {
  
  dat <- dat[which(dat$Var2_source == query & dat$Var1_source == reference), ]
  
  p <- ggplot(data = dat, 
              aes(Var1, Var2, fill = value)) +
    geom_tile(colour = "white") +
    scale_fill_gradient(low = 'white', high = 'red',
                        name = "Spearman\nCorrelation") +
    geom_point(aes(Var1, Var2, alpha = top3),
               size = 1.5, shape = 19, stroke  = 0) +
    scale_alpha_manual(values = c(1, 0.5, 0.25), 
                       breaks = c(1, 2, 3),
                       name = "top3", na.value = 0) +
    coord_fixed(ratio = 1) +
    xlab("reference") +
    ylab("query") +
    labs(caption = "For each celltype in query, black points represent top 3 celltypes from reference with highest correlation.",
         title = paste0(query, " vs. ", reference)) +
    theme_bw() +
    theme(axis.text.x = element_text(angle = 90, size = 8, 
                                     hjust=1, vjust = 0.5),
          axis.text.y = element_text(size=8),
          axis.ticks.length = unit(0.15, units = c('lines')),
          legend.title = element_text(size = 10),
          axis.title = element_text(size = 8),
          plot.caption = element_text(size = 7),
          panel.grid = element_blank(),
          panel.border = element_blank(),
          legend.key.size = unit(0.8, units = c('lines')))
  
  return(p)
}

3.1.1 Against Vento-Tormo et al

## Annotation plots
anno.vento <- clustAnnoPlot(dat = cormat, query = "clust", reference = "vento")
cowplot::ggsave2(anno.vento, device = "pdf", width = 7.5, height = 6.5, units = "in",
                 filename = "../results/02_annotation/plots/annotation-by-correlation_ref-vento.pdf")
print(anno.vento)

3.1.2 Against Suryavanshi et al

## Annotation plots
anno.surya <- clustAnnoPlot(dat = cormat, query = "clust", reference = "surya")
cowplot::ggsave2(anno.surya, device = "pdf", width = 7.5, height = 6.5, units = "in",
                 filename = "../results/02_annotation/plots/annotation-by-correlation_ref-surya.pdf")
print(anno.surya)

3.1.3 Against Pavlicev et al

## Annotation plots
anno.pavli <- clustAnnoPlot(dat = cormat, query = "clust", reference = "pavli")
cowplot::ggsave2(anno.pavli, device = "pdf", width = 7, height = 6, units = "in",
                 filename = "../results/02_annotation/plots/annotation-by-correlation_ref-pavli.pdf")
print(anno.pavli)

3.2 Top matches

topmatch %>% kable(caption = "Top 3 matches against all reference datasets.") %>% kable_styling(full_width = FALSE, font_size = 10)
Top 3 matches against all reference datasets.
cluster vento.top1 surya.top1 pavli.top1 vento.top2 surya.top2 pavli.top2 vento.top3 surya.top3 pavli.top3
clust_00 dS3 dec.DSC DEC dS2 dec.FB1 ESF dP2 dec.FB2 SYN
clust_01 dM2 dec.MAC SYN dM1 vil.HC DEND HB dec.DC2 DEC
clust_02 dP2 dec.FB1 DEC dS2 dec.FB2 SYN dS3 dec.DSC ESF
clust_03 dM1 dec.MAC SYN MO vil.HC DEND dM2 dec.NK1 DEC
clust_04 Endo.m dec.VEC DEC Endo.L dec.LEC ESF Endo.f vil.VEC SYN
clust_05 dNK3 dec.NK1 DEC dNK2 dec.TC ESF Tcells dec.MAC SYN
clust_06 fFB1 dec.FB1 DEC dP2 vil.FB3 SYN dS2 dec.FB2 ESF
clust_07 fFB1 vil.FB3 DEC dP2 dec.FB1 ESF dS2 dec.FB2 SYN
clust_08 dM1 dec.MAC DEND MO vil.HC SYN dM2 dec.DC2 DEC
clust_09 Tcells dec.TC DEC dNK3 dec.NK1 ESF dNK2 dec.NK2 SYN
clust_10 dNK3 dec.NK1 DEC dNK2 dec.TC ESF dNK1 dec.NK2 SYN
clust_11 dM1 dec.MAC DEND dM2 vil.HC DEC dM3 dec.DC2 SYN
clust_12 dM1 dec.MAC SYN dM2 vil.HC DEND dM3 dec.DC2 DEC
clust_13 EVT vil.EVT EVT SCT dec.DSC DEC dP2 dec.FB1 ESF
clust_14 dNK3 dec.NK1 DEC dNK2 dec.TC ESF dNK1 dec.NK2 SYN
clust_15 dP2 dec.MAC DEC dM1 dec.FB2 SYN fFB1 dec.FB1 ESF
clust_16 VCT vil.VCT CYT2 SCT vil.SCT CYT3 EVT vil.EVT CYT1
clust_17 VCT vil.VCT CYT2 SCT vil.SCT CYT3 EVT vil.EVT CYT1
clust_18 dS3 dec.DSC DEC dS2 dec.FB1 ESF dS1 dec.FB2 CYT2
clust_19 dP1 dec.SMC DEC dP2 vil.FB2 ESF Endo.m dec.FB2 SYN
clust_20 Plasma dec.MAC SYN dM1 dec.NK1 DEC dNK3 dec.TC DEND
clust_21 Endo.m dec.VEC DEC Endo.f vil.VEC SYN Endo.L dec.LEC ESF
clust_22 dM2 dec.MAC SYN dM1 vil.HC DEC HB dec.DC2 DEND
clust_23 SCT vil.VCT CYT2 VCT vil.SCT CYT3 EVT vil.EVT CYT1
clust_24 SCT vil.SCT CYT2 VCT vil.VCT CYT3 EVT vil.EVT SYN
clust_25 VCT vil.VCT CYT2 SCT vil.SCT CYT3 EVT vil.EVT CYT1
clust_26 dM1 dec.MAC DEC dM2 vil.HC SYN dM3 dec.VEC ESF
clust_27 dM2 dec.MAC SYN fFB1 vil.HC DEC HB vil.FB3 ESF
clust_28 MO dec.MAC SYN dM1 vil.HC CYT2 dM2 dec.NK2 DEND
clust_29 SCT vil.SCT CYT2 VCT vil.VCT CYT1 EVT vil.EVT CYT3
clust_30 dNK.p dec.NK2 ESF dNK1 dec.NK1 DEND dNK3 dec.MAC CYT2
clust_31 dM2 dec.MAC SYN dM1 vil.HC DEND MO dec.NK1 CYT2
clust_32 Tcells dec.TC SYN dNK3 dec.NK1 DEC dM1 dec.MAC DEND
clust_33 fFB1 vil.FB3 SYN dP2 dec.SMC DEC dP1 dec.FB1 ESF
clust_34 dM2 dec.MAC SYN dM1 vil.HC DEND HB dec.DC2 DEC
NA

3.3 Labels for clusters

We need to create consistent labels for all cell types that we identify. Below is a list I created in which the top level objects are labels we will give to the cell types, within which contained are lists of corresponding labels from vento and surya. These are tentative labels. After the first pass, when we go through the clusters with a fine-toothed comb, we can modify them further. For example, if we have multiple clusters labeled as dec.DSC (decidual macrophages), and if those clusters are distinct, we can then break up this label into dec.DSC1 and dec.DSC2.

labs <- list("dec.DSC"    = list("vento.lab" = c("dS1", "dS2", "dS3"),
                                 "surya.lab" = c("dec.DSC", "dec.FB1", "dec.FB2")),
             "DC"     = list("vento.lab" = c("DC1", "DC2"),
                                 "surya.lab" = c("dec.DC1", "dec.DC2")),
             "Mac"    = list("vento.lab" = c("dM1", "dM2", "dM3"),
                                 "surya.lab" = c("dec.MAC")),
             "NK"     = list("vento.lab" = c("dNK.p", "dNK1", "dNK2", "dNK3", "NK.CD16neg", "NK.CD16pos"),
                                 "surya.lab" = c("dec.NK1", "dec.NK2")),
             "dec.SMC"    = list("vento.lab" = c("dP1", "dP2"),
                                 "surya.lab" = c("dec.SMC")),
             "dec.Endo"   = list("vento.lab" = c("Endo.m"),
                                 "surya.lab" = c("dec.VEC")),
             "dec.Epi"    = list("vento.lab" = c("Epi1", "Epi2"),
                                 "surya.lab" = c("dec.EEC")),
             "Tcell"  = list("vento.lab" = c("Tcells"),
                                 "surya.lab" = c("dec.TC")),
             "vil.Endo"   = list("vento.lab" = c("Endo.f"),
                                 "surya.lab" = c("vil.VEC")),
             "vil.EVT"    = list("vento.lab" = c("EVT"),
                                 "surya.lab" = c("vil.EVT")),
             "vil.FB"     = list("vento.lab" = c("fFB1", "fFB2"),
                                 "surya.lab" = c("vil.FB1", "vil.FB2", "vil.FB3")),
             "vil.Hofb"   = list("vento.lab" = c("HB"),
                                 "surya.lab" = c("vil.HC")),
             "vil.SCT"    = list("vento.lab" = c("SCT"),
                                 "surya.lab" = c("vil.SCT")),
             "vil.VCT"    = list("vento.lab" = c("VCT"),
                                 "surya.lab" = c("vil.VCT")),
             "Gran"   = list("vento.lab" = c("Granulocytes"),
                                 "surya.lab" = c()),
             "ILC"    = list("vento.lab" = c("ILC3"),
                                 "surya.lab" = c()),
             "Mono"   = list("vento.lab" = c("MO"),
                                 "surya.lab" = c()),
             "Plasma" = list("vento.lab" = c("Plasma"),
                                 "surya.lab" = c()),
             "EB"     = list("vento.lab" = c(),
                                 "surya.lab" = c("vil.EB")),
             "unk.Endo.L" = list("vento.lab" = c("Endo.L"),
                                 "surya.lab" = c("dec.LEC"))
)

3.4 Matches consistent between reference datasets

The following clusters have consistent top1 match between vento and surya references, i.e. they correspond to the same cell type category. The pavli reference is too unresolved to be used diagnostically, so we will ignore it for now. For some cell types it is confirmatory, though, e.g. cluster_00 corresponds to DSC in all three references.

# find out which clusters are consistent.
# If vento.top1 and surya.top1 are found in the same item in the labs list, the mapping is consistent.
consistent <- c(NA)
for(i in 1:nrow(topmatch)){
  consistent[i] <- grep(topmatch$vento.top1[i], labs) == grep(topmatch$surya.top1[i], labs)
}

# subset to include consistent set
topmatch.cons <- topmatch %>% 
  filter(consistent) %>% 
  dplyr::select(cluster, vento.top1, surya.top1)

# add our tentative labels to the clusters
for(i in 1:nrow(topmatch.cons)){
  topmatch.cons$label[i] <- names(labs)[grep(topmatch.cons$vento.top1[i], labs)]
}

# print
topmatch.cons[order(topmatch.cons$label), ] %>% knitr::kable() %>% kable_styling(full_width = FALSE)
cluster vento.top1 surya.top1 label
1 clust_00 dS3 dec.DSC dec.DSC
16 clust_18 dS3 dec.DSC dec.DSC
4 clust_04 Endo.m dec.VEC dec.Endo
18 clust_21 Endo.m dec.VEC dec.Endo
17 clust_19 dP1 dec.SMC dec.SMC
2 clust_01 dM2 dec.MAC Mac
3 clust_03 dM1 dec.MAC Mac
7 clust_08 dM1 dec.MAC Mac
10 clust_11 dM1 dec.MAC Mac
11 clust_12 dM1 dec.MAC Mac
19 clust_22 dM2 dec.MAC Mac
22 clust_26 dM1 dec.MAC Mac
23 clust_27 dM2 dec.MAC Mac
26 clust_31 dM2 dec.MAC Mac
29 clust_34 dM2 dec.MAC Mac
5 clust_05 dNK3 dec.NK1 NK
9 clust_10 dNK3 dec.NK1 NK
13 clust_14 dNK3 dec.NK1 NK
25 clust_30 dNK.p dec.NK2 NK
8 clust_09 Tcells dec.TC Tcell
27 clust_32 Tcells dec.TC Tcell
12 clust_13 EVT vil.EVT vil.EVT
6 clust_07 fFB1 vil.FB3 vil.FB
28 clust_33 fFB1 vil.FB3 vil.FB
20 clust_24 SCT vil.SCT vil.SCT
24 clust_29 SCT vil.SCT vil.SCT
14 clust_16 VCT vil.VCT vil.VCT
15 clust_17 VCT vil.VCT vil.VCT
21 clust_25 VCT vil.VCT vil.VCT

3.5 Ambiguous clusters

The top1 matches for some clusters with vento and surya are not the same. The are the clusters that will require a more detailed look to determine their identify.

# subset for ambiguous clusters
topmatch.ambig <- topmatch %>% 
  filter(!consistent) %>% 
  dplyr::select(cluster, vento.top1, surya.top1)

# add our labels.
for(i in 1:nrow(topmatch.ambig)){
  topmatch.ambig$label[i] <- paste0(
    names(labs)[grep(topmatch.ambig$vento.top1[i], labs)],
    " or ",
    names(labs)[grep(topmatch.ambig$surya.top1[i], labs)]
    )
}

# print
topmatch.ambig[order(topmatch.ambig$label), ] %>% kable() %>% kable_styling(full_width = FALSE)
cluster vento.top1 surya.top1 label
1 clust_02 dP2 dec.FB1 dec.SMC or dec.DSC
3 clust_15 dP2 dec.MAC dec.SMC or Mac
6 clust_28 MO dec.MAC Mono or Mac
4 clust_20 Plasma dec.MAC Plasma or Mac
2 clust_06 fFB1 dec.FB1 vil.FB or dec.DSC
5 clust_23 SCT vil.VCT vil.SCT or vil.VCT

4 Annotation refinement

The annotations we have so far are only a starting point. Now we can look at each annotation more carefully to make sure that everything makes sense.

4.1 Sample contribution to clusters

One of the ways in which we can refine the clusters further is by knowing which tissue that sample arises from. For example, if a cluster has macrophage gene signature and if most cells in that cluster come from villi samples, we can further narrow down the identity of the cluster to Hofbauer cells. We can do this by simply counting for each cluster how many cells come from decidua vs villi and by calculating the percentage.

# count cells in each cluster by tissue of origin
bytissue <- table(seur$tissue, seur$seurat_clusters) %>% 
  as.data.frame() %>% 
  dplyr::rename(tissue = Var1, cluster = Var2, frequency = Freq)

# calculate fraction
for(i in 1:nrow(bytissue)){
  bytissue$fraction[i] <- bytissue$frequency[i]/
    sum(bytissue$frequency[bytissue$cluster == bytissue$cluster[i]])
}

# plot
p.cells <- ggplot(data = bytissue, aes(x = frequency, y = cluster)) +
  geom_bar(aes(fill = tissue), stat = "identity", position = "stack") +
  ggtitle("Number of cells")

p.frac <- ggplot(data = bytissue, aes(x = fraction, y = cluster)) +
  geom_bar(aes(fill = tissue), stat = "identity", position = "stack") +
  ggtitle("Fraction of cells")

p.cells + p.frac + plot_layout(guides = "collect", nrow = 2) +
  plot_annotation(caption = "Sample contribution to clusters") &
  coord_flip() &
  scale_fill_tableau(palette = "Classic 10 Medium") &
  theme_minimal() +
  theme(panel.grid.minor = element_blank(),
        panel.grid.major = element_line(size = 0.25),
        plot.title = element_text(size = 10),
        axis.text.x = element_text(angle = 90, vjust = 0.5))

NA
NA

Following clusters have more than 70% cells coming from the same tissue of origin.

bytissue[, c("cluster", "tissue", "fraction")] %>% 
  pivot_wider(names_from = "tissue", values_from = "fraction") %>% 
  filter(decidua > 0.70 | villi > 0.70) %>% 
  kable(digits = 2, row.names = FALSE, 
        caption = "Clusters with more than 70% cells from one tissue") %>%
  kable_styling(full_width = FALSE)
Clusters with more than 70% cells from one tissue
cluster decidua villi
clust_00 0.98 0.02
clust_01 0.13 0.87
clust_03 0.15 0.85
clust_04 0.80 0.20
clust_05 0.93 0.07
clust_06 0.19 0.81
clust_07 0.21 0.79
clust_08 0.15 0.85
clust_12 0.77 0.23
clust_13 0.80 0.20
clust_15 0.78 0.22
clust_16 0.08 0.92
clust_17 0.01 0.99
clust_18 0.92 0.08
clust_20 0.15 0.85
clust_22 0.05 0.95
clust_23 0.13 0.87
clust_24 0.02 0.98
clust_25 0.01 0.99
clust_26 0.20 0.80
clust_27 0.02 0.98
clust_28 0.03 0.97
clust_29 0.05 0.95
clust_31 0.01 0.99
clust_33 0.04 0.96
clust_34 0.03 0.97

Following clusters have ambiguous origin, i.e. they contain cells from decidua and villi samples.

bytissue[, c("cluster", "tissue", "fraction")] %>% 
  pivot_wider(names_from = "tissue", values_from = "fraction") %>% 
  filter(decidua < 0.70 & villi < 0.70) %>% 
  kable(digits = 2, row.names = FALSE, 
        caption = "Clusters with cells from both tissues") %>%
  kable_styling(full_width = FALSE)
Clusters with cells from both tissues
cluster decidua villi
clust_02 0.65 0.35
clust_09 0.44 0.56
clust_10 0.43 0.57
clust_11 0.35 0.65
clust_14 0.59 0.41
clust_19 0.54 0.46
clust_21 0.64 0.36
clust_30 0.67 0.33

This is largely consistent with the putative annotations. All putatively trophoblast clusters come overwhelmingly from villi samples. The only exception is extravillous trophoblast cluster, which as expected, comes from decidua samples. At least in the species that I have worked with, it is often difficult to separate the fetal tissue from maternal tissue. Therefore, we use this analysis only as a rough guide, i.e. when there is a conflict between the two, the marker gene-based annotation should take precedence over tissue of origin in cell type id because the latter is experimentally (and as a consequence, analytically) messy to disentangle.

Most clusters that have mixed origin, are putative immune cell type clusters. This could suggest that the immune cell types are present on both maternal and fetal sides of the interface, and because they are the same cell type, they end up in the same cluster. This means that we can’t confidently say for immune cells whether they are decidual or villous. For this reason, when such ambiguity is present, we will label them without the tissue prefix, i.e. for example Tcell instead of dec.Tcell or vil.Tcell.

4.2 Marker genes

4.2.1 Identify marker genes

I ran Seurat::findAllMarkers on this data using the SCT assay. This function outputs a list of marker genes for each cluster compared to all other cells in the data. This takes a while (hours) to run, so I ran it on the cluster and saved the results to a .csv. See the code directory for full code, and see below for the function call.

# set default assay to SCT
DefaultAssay(object = plac) <- "SCT"

# find markers for all clusters
markers <- FindAllMarkers(object = plac, 
                          only.pos = TRUE,
                          logfc.threshold = 0.25)  

# write.csv
write.csv(markers, "/home/arc78/scratch60/covid-placenta/markers_sct.csv", row.names = FALSE)
markers <- read.csv("../results/02_annotation/files/markers_sct.csv")

# rename clusters
markers$cluster <- paste0("clust_", markers$cluster)
markers$cluster <- gsub("(clust_)(\\d)$", "\\10\\2", markers$cluster)
markers$cluster <- factor(markers$cluster, levels = unique(markers$cluster))

# split by cluster
markers <- split.data.frame(markers, markers$cluster)

4.2.2 Plot marker genes

We will plot top 50 marker genes for all clusters and save the plot to pdf.

# plotting function
DotPlot2 <- function(object = seur, assay = "SCT", features, title, ...) {
  p <- DotPlot(object, 
               assay = assay,
               features = features, 
               dot.min = 0.05, dot.scale = 4) +
    coord_flip() +
    labs(caption = paste0(assay, " normalized expression"), title = title) +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1),
          panel.grid = element_blank())
  
  return(p)
}
top50marker.plots <- list()
for(i in names(markers)) {
  if(nrow(markers[[i]]) > 50)
    features <- markers[[i]]$gene[1:50]
  if(nrow(markers[[i]]) < 50)
    features <- markers[[i]]$gene

  top50marker.plots[[i]] <- DotPlot2(features = features, title = i)
         
  cowplot::ggsave2(top50marker.plots[[i]], 
                   filename = paste0("../results/02_annotation/plots/top50markers_dotplot_", i, ".pdf"),
                   width = 7.5, height = 7.5, units = "in")
}

5 Manual annotation

With the marker gene plots we have made, we can examine the top marker genes in each cluster to manually confirm or adjust the annotation of each cluster.

Let’s make a dataframe in which to keep track of the assignments.

annotation <- data.frame(
  cluster = unique(seur$seurat_clusters) %>% sort(),
  annotation = NA,
  notes = NA
)

For each set of marker genes, we can also perform a number of manual checks to assess cell type annotation.

First is GO enrichment using clusterProfiler package, for which below is a function for quickly looking at top 10 enriched GO categories.

# function for GO enrichment
# 1. for a given cluster, prints n.print.rows top enriched GO terms
# 2. Can exclude ribosomal genes (default) because they dominate some GO enrichments
enrichGO2 <- function(clust, ngenes = 100, include.ribo = FALSE, n.print.rows = 10, ...) {
  
  ids.bitr <- bitr(markers[[clust]]$gene[1:ngenes], 
                fromType = "SYMBOL", toType = c("ENTREZID", "SYMBOL"), 
                OrgDb = "org.Hs.eg.db")
    
  if(include.ribo == FALSE)
    features <- ids.bitr$ENTREZID[!(grepl("RP[SL]", ids.bitr$SYMBOL))]
  if(include.ribo == TRUE)
    features <- ids.bitr$ENTREZID

  ego <- enrichGO(
    gene          = features,    
    OrgDb         = org.Hs.eg.db,
    ont           = "BP",
    pAdjustMethod = "BH",
    pvalueCutoff  = 0.01, # default 0.01
    qvalueCutoff  = 0.05, # default 0.05
    readable = TRUE)
  
  dftoprint <- clusterProfiler::simplify(ego)@result[1:n.print.rows, c(2,8)]
  rownames(dftoprint) <- NULL
  
  return(dftoprint)
}

We can also check if the cluster is enriched is genes that are labeled as markers genes for a cell type by other studies. (Vento-Tormo et al. 2018) have provided top 30 marker genes for all cell types identified by them.

# read top 30 markers from vento-tormo et al
markers.vento <- readxl::read_excel("../ext/from-papers/vento-tormo_2018/41586_2018_698_MOESM1_ESM/Supplementary Table 2.xlsx")

# rename columns
markers.vento <- dplyr::rename(markers.vento,
                               VCT_1 = VCT...2,
                               Tcells_1 = `T cells...5`,
                               Tcells_2 = `T cells...9`,
                               VCT_2 = `VCT...10`,
                               Tcells_3 = `T cells...11`,
                               FB_1 = F1,
                               EVT_1 = `EVT...14`,
                               Tcells_4 = `T cells...15`,
                               EVT_2 = `EVT...16`,
                               NKCD16pos = `Blood NK CD16+`,
                               MO_1 = `MO...22`,
                               MO_2 = `MO...27`,
                               NKCD16neg = `Blood NK CD16-`,
                               FB2 = F2,
                               Endo.m = `Endo (m)`,
                               Endo.L = `Endo L`,
                               Endo.f = `Endo (f)` 
)

# remove ensembl ids
markers.vento <- apply(X = markers.vento, MARGIN = 2, FUN = function(x) gsub("_ENSG\\d+", "", x)) %>% 
  as.data.frame()

(Suryawanshi et al. 2018) have also provided a list of top30 marker genes in the supplementary data.

markers.surya.vil <- readxl::read_excel("../ext/from-papers/suryawanshi_2018/supp-data/aau4788_Data_file_S3.xlsx", sheet = "Villi", range = "A1:G271", trim_ws = TRUE)
markers.surya.dec <- readxl::read_excel("../ext/from-papers/suryawanshi_2018/supp-data/aau4788_Data_file_S3.xlsx", sheet = "Deciuda", range = "A1:G331", trim_ws = TRUE)

markers.surya.vil <- dplyr::rename(markers.surya.vil, cluster = `cell type`)

markers.surya.vil$cluster <- paste0("vil.", markers.surya.vil$cluster)
markers.surya.dec$`cluster` <- paste0("dec.", markers.surya.dec$`cluster`)

markers.surya <- rbind(markers.surya.vil, markers.surya.dec)

5.1 DSC

Clusters 0 and 18.

5.1.1 Notes

  1. Clusters 0 and 18 highly correlated with each other and most similar to each other than to other clusters.
  2. Both are consistently similar to the decidual stromal cells in both vento and surya datasets.
  3. The marker genes include Prolactin, IGFBP1 etc, which are typical of decidual stromal cells.
  4. These two clusters also arise unabiguously from the decidua samples rather than villi samples.
annotation$annotation[annotation$cluster %in% c("clust_00")] <- "dec.DSC_1"
annotation$annotation[annotation$cluster %in% c("clust_18")] <- "dec.DSC_2"

annotation[annotation$cluster %in% paste0("clust_", c("00", "18")), ]

5.1.2 Marker genes plots

top50marker.plots[["clust_00"]]

top50marker.plots[["clust_18"]]

5.1.3 Go anrichment

enrichGO2("clust_00")
'select()' returned 1:1 mapping between keys and columns

5.1.4 Vento markers

DotPlot2(features = markers.vento$dS3, title = "vento: dS3")

5.1.5 Surya markers

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.DSC"], title = "surya markers: DSC")

5.2 Endothelial cells

Clusters 4 and 21.

5.2.1 Notes

  1. Clusters 4 and 21 are highly correlated.
  2. They consistently get assigned as decidual endothelial cells by both vento and surya datasets.
  3. More than 75% of the cells in both of these clusters come from the decidua samples.
  4. Markers genes of both clusters are enriched in GO categories related to epithelium development, endothelium development etc.
  5. But these two clusters are not as alike as clusters 0 and 18. They are different in terms of their expression of many endothelium related genes like CD34, PROX1, VWF, SPARCL. Cluster 21 looks more like “typical” endothelial cells. Cluster 4 has low expression of many typical endothelial genes.
  6. Cluster 4 has higher expression of some lymphatic endothelial cells: PROX1, LYVE1. Many genes from the list of surya marker genes for dec.LEC are expressed in cluster 4. This suggests that Cluster 4 may be lymphatic endothelial cells and cluster 21 may be endothelial cells.
annotation$annotation[annotation$cluster %in% c("clust_04")] <- "dec.Endo.L"
annotation$annotation[annotation$cluster %in% c("clust_21")] <- "dec.Endo"

annotation[annotation$cluster %in% paste0("clust_", c("04", "21")), ]

5.2.2 Marker gene plots

top50marker.plots[["clust_04"]]

top50marker.plots[["clust_21"]]

5.2.3 GO enrichment

enrichGO2("clust_04")
'select()' returned 1:1 mapping between keys and columns
2% of input gene IDs are fail to map...
enrichGO2("clust_21")
'select()' returned 1:1 mapping between keys and columns
2% of input gene IDs are fail to map...

5.2.4 Vento markers

DotPlot2(features = markers.vento$Endo.m, title = "vento: Endo.m")

DotPlot2(features = markers.vento$Endo.f, title = "vento: Endo.f")

DotPlot2(features = markers.vento$Endo.L, title = "vento: Endo.L")

5.2.5 Surya markers

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.VEC"], title = "surya markers: vil.VEC")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.VEC"], title = "surya markers: dec.VEC")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.LEC"], title = "surya markers: dec.LEC")

5.3 EVT

Cluster 13.

5.3.1 Notes

  1. This cluster is unique in its expression of HLA-G, typical of extravillous trophoblast.
  2. Several other genes often enriched in EVT are also enriched in this cluster: FN1, PAPPA2, DIO2, etc.
  3. top 30 markers associated with EVT_2 from vento dataset are also highly and specifically expressed in this cluster.
  4. It is consistently assigned to be EVT based on vento, surya, and pavli datasets.

Annotation:
Cluster 13: Extravillous trophoblast (vil.EVT)

annotation$annotation[annotation$cluster %in% c("clust_13")] <- "vil.EVT"

annotation[annotation$cluster %in% c("clust_13"), ]

5.3.2 Marker genes plot

top50marker.plots[["clust_13"]]

5.3.3 GO enrichment

enrichGO2("clust_13")
1% of input gene IDs are fail to map...

5.3.4 Vento markers

DotPlot2(features = markers.vento$EVT_1, title = "vento markers: EVT_1")

DotPlot2(features = markers.vento$EVT_2, title = "vento markers: EVT_2")

5.3.5 Surya markers

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.EVT"], title = "surya markers: vil.EVT")

5.4 VCT and SCT

clusters 16, 17, 23, 24, 25, and 29.

5.4.1 Notes

For more information on expression patterns and markers of different trophoblast types, see (Liu et al. 2018).

  1. In addition to cluster 13 (EVT), there are 6 other clusters that are likely trophoblast cell types (they express KRT7 and are related clusters). These 6 clusters are organized into two correlated blocks (see “correlations section above”): (16, 17, 25) and (23, 29, 24) of which the former are likely VCT and the latter SCT according to correlation-based annotation.
  2. Cluster 29 appears to be SCT given its expression of CGA, GDF15, ERVFRD-1 (Syncytin 2) genes. For some reason GO enrichment didn’t work for markers of cluster 29, but it does consistently express SCT markers from vento and surya.
  3. Cluster 24 doesn’t express ERVFRD-1 much, but it does express most other telltale genes of SCT: CSH1, CSH2, CGA, CSHL1, many PSG genes, GH2, PGF, GDF15, etc. And it is not enriched for expression of VCT markers from vento and surya. It’s enriched GO terms have to do with hormone production and signaling as expected for SCT. Thus, this is most likely an SCT cluster.
  4. Clusters 16 and 17 don’t express many markers of SCT, but do express many vento and surya markers of VCT: PAGE4, PEG10, XAGE3, etc.
  5. Cluster 25 has VCT markers as well as a strong signature of cell division genes. This shows up in the GO enrichment as well, where most enriched GO terms are “nuclear division”, “chromosome segregation”, “cell cycle G1/S phase transition” etc. This is likely a cluster of proliferating VCT cells. Incidentally both (Vento-Tormo et al. 2018) and (Liu et al. 2018) have a separate population of VCT with a cell cycle gene signature (VCT_2 in vento).
  6. Cluster 23 is most similar to SCT of vento but VCT of surya. Indeed this cluster has expression of marker genes for both VCT and SCT (see plots). It expresses PAGE4, PEG10 etc, but also expresses syncytin gene ERVW-1. This could represent a group of cytotrophoblast cells that are in the process of differentiating to sycnytial trophoblast. We will label it as a VCT type.
annotation$annotation[annotation$cluster %in% c("clust_16")] <- "vil.VCT_1"
annotation$annotation[annotation$cluster %in% c("clust_17")] <- "vil.VCT_2"
annotation$annotation[annotation$cluster %in% c("clust_23")] <- "vil.VCT_3"
annotation$annotation[annotation$cluster %in% c("clust_25")] <- "vil.VCT_4"
annotation$annotation[annotation$cluster %in% c("clust_24")] <- "vil.SCT_1"
annotation$annotation[annotation$cluster %in% c("clust_29")] <- "vil.SCT_2"

annotation$notes[annotation$cluster %in% c("clust_25")] <- "proliferating"

annotation[annotation$cluster %in% paste0("clust_", c("16", "17", "23", "25", "24", "29")), ]

5.4.2 Marker gene plots

top50marker.plots[["clust_16"]]

top50marker.plots[["clust_17"]]

top50marker.plots[["clust_25"]]

top50marker.plots[["clust_23"]]

top50marker.plots[["clust_24"]]

top50marker.plots[["clust_29"]]

5.4.3 GO enrichment

enrichGO2("clust_16")
enrichGO2("clust_17")
2.5% of input gene IDs are fail to map...
enrichGO2("clust_25")
3% of input gene IDs are fail to map...
enrichGO2("clust_23")
enrichGO2("clust_24")
1% of input gene IDs are fail to map...
enrichGO2("clust_29")
2% of input gene IDs are fail to map...

5.4.4 Vento markers

DotPlot2(features = markers.vento$VCT_1, title = "vento markers: VCT_1")

DotPlot2(features = markers.vento$VCT_2, title = "vento markers: VCT_2")

DotPlot2(features = markers.vento$SCT, title = "vento markers: SCT")

5.4.5 Surya markers

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.VCT"], title = "surya markers: vil.VCT")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.SCT"], title = "surya markers: vil.SCT")

5.4.6 Pavli markers

DotPlot2(features = c("ADRB1", "PUF60", "SNORDA3A", "PRMT7", "E1F1AY", 
                      "FXYD3", "GRAMD2", "INSL4", "ITGB8", "PAGE4",
                      "SLC13A4", "SLC22A11"), 
         title = "pavli markers: VCT_1")

DotPlot2(features = c("XAGE3", "XAGE2", "SERINC5", "S100P", "RASA1",
                      "MFAP5", "LIN28B", "KRT8", "KRT7", "INS-IGF2", "GCM1",
                      "GATA3", "ERVW-1", "ERVFRD-1", "EGR1", "EFNA1", 
                      "CYP19A1", "PHLDA2", "ACKR2"), 
         title = "pavli markers: VCT_2")

DotPlot2(features = c("SEMA3B", "CSH2", "CSHL1", "FCGR2A", "GH2", "HBA1", 
                      "HBA2", "HBB", "HBG1", "HBG2", "HLA-DMA", "HLA-DPA1",
                      "HLA-DQB1", "HLA-DRA", "HLA-DRB1", "HPGDS", "CGB8",
                      "LGALS14", "LYVE1", "NPIPB3", "CGB5", "PSG1", "PSG3", 
                      "PSG6", "PSG9", "ZNF117", "ZNF91", "HIST2H2AB",
                      "RAMP2", "MUC20"), 
         title = "pavli markers: SCT")

5.4.7 Liu et al markers for trophoblasts

DotPlot2(seur, features = c("GCM1", "CSH1", "KRT7", "TFAP2C", "GATA3", "CDH1", "EGFR", "HLA-G", "MMP2", "RRM2", "CCNB1", "CDK1", "ERVFRD-1", "SLC1A5", "PAGE4", "CGB"), title = "Genes from Liu et al 2018")
The following requested variables were not found: CGB

5.5 Fibroblasts and SMC

Clusters 02, 06, 07, 15, 19, and 33.

5.5.1 Notes

  1. Most of these clusters represent some kind of fibroblast or a closely related cell type. Most clusters are enriched for GO terms related to ECM regulation.
  2. Clusters 02 and 15 marker genes are also highly expressed in clusters 00 and 18. Cluster 15 has more than 75% cells coming from decidua samples, and cluster 02 has over 50% cells coming from decidua samples. Decidual stromal cells are known to be actually a few different populations. Even in Vento-tormo et al, they are labeled as dS1–3, of which dS3 are the canonical DSC with PRL and IGFBP1, while dS1 and dS2 are closely related to DSC and are derived from endometrial stromal fibroblasts. These things together suggest that clusters 02 and 15 are the non-PRL varieties of DSC. To distinguish them from DSC, we can call them decidual fibroblasts.
  3. Marker genes of 06, 07, and 33 show highly correlated gene expressin, i.e. the markers on each of these clusters are also highly expressed in the other two clusters, suggesting that these three clusters a closely related cell populations. All three of these clusters are largely (over 75% of the cells) derived from villi samples, and they are fibroblast-like cells. Thus, these are likely villous fibroblasts.
  4. Cluster 19 is most similar to PV (perivascular) clusters from vento and SMC (smooth muscle cell) cluster from surya. Indeed, its markers include smooth muscle genes like MGP, MYH11, MYL9 etc, and enriched GO terms for this cluster include “muscle contraction” and “artery morphogenesis”. Thus, this cluster is most likely of perivascular smooth muscle cells of decidual origin (over 75% cells from decidua samples).
annotation$annotation[annotation$cluster %in% c("clust_02")] <- "dec.FB_1"
annotation$annotation[annotation$cluster %in% c("clust_15")] <- "dec.FB_2"
annotation$annotation[annotation$cluster %in% c("clust_06")] <- "vil.FB_1"
annotation$annotation[annotation$cluster %in% c("clust_07")] <- "vil.FB_2"
annotation$annotation[annotation$cluster %in% c("clust_33")] <- "vil.FB_3"
annotation$annotation[annotation$cluster %in% c("clust_19")] <- "dec.SMC"

annotation[annotation$cluster %in% paste0("clust_", c("02", "15", "06", "07", "33", "19")), ]

5.5.2 Marker gene plots

top50marker.plots[["clust_02"]]

top50marker.plots[["clust_15"]]

top50marker.plots[["clust_06"]]

top50marker.plots[["clust_07"]]

top50marker.plots[["clust_19"]]

top50marker.plots[["clust_33"]]

5.5.3 GO enrichment

enrichGO2("clust_02")
10.77% of input gene IDs are fail to map...
enrichGO2("clust_15")
2.56% of input gene IDs are fail to map...
enrichGO2("clust_06")
1% of input gene IDs are fail to map...
enrichGO2("clust_07")
3% of input gene IDs are fail to map...
enrichGO2("clust_19")
2% of input gene IDs are fail to map...
enrichGO2("clust_33")
3% of input gene IDs are fail to map...

5.5.4 Vento markers

DotPlot2(features = markers.vento$PV1, title = "vento markers: PV1")

DotPlot2(features = markers.vento$PV2, title = "vento markers: PV2")

5.5.5 Surya markers

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.FB1"], title = "surya markers: vil.FB1")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.SMC"], title = "surya markers: dec.SMC")

5.6 B cells

Cluster 20.

5.6.1 Notes

This is most likely a cluster of B cells. Most markers genes are related to B cells: many immunoglobulin gnees, SPIB, MS4A1, CD79A, CD79B, BANK1, etc. It also expresses many genes that are markers for Plasma cluster from vento dataset. GO enrichment also agrees.

annotation$annotation[annotation$cluster %in% c("clust_20")] <- "Bcell"

annotation[annotation$cluster %in% paste0("clust_", c("20")), ]

5.6.2 Marker genes plot

top50marker.plots[["clust_20"]]

5.6.3 GO enrichment

enrichGO2("clust_20")
1% of input gene IDs are fail to map...

5.6.4 Vento markers

DotPlot2(features = markers.vento$Plasma, title = "vento markers: Plasma")

5.7 Other lymphocytes

Clusters 05, 09, 10, 14, 30, and 32.

5.7.1 Notes

For clues on marker genes for various types of lymphocytes see (Pizzolato et al. 2019).

  1. These are likely non-B lymphoid cells.
  2. Clusters 09 and 32 are most similar to Tcells from both vento and surya. Given the expression of CD3 genes, that appears to be the correct assignment.
    • The clusters also express TRAC and TRBC1 (constant regions of TCR alpha and beta), making them alpha-beta T cells.
    • It’s hard to tell if they are CD4 or CD8—both CD4 and CD8 genes are expressed by a small fraction of cells in both clusters, but CD8 genes expressed at a higher level.
    • GO enrichment for both include terms related to T cell differentiation, T cell activation etc.
    • These clusters contain cells from both decidua and villi samples.
  3. Cluster 05 is most similar to NK cells from vento and surya. But based on the lack of NKG7 and NCAM1 expression and expression of CD3 genes, TRAC, and TRBC1 this is likely a T cell cluster. One of its marker genes being GZMA and its overall similarity with NK cells suggest that this may be a cytotoxic T cell. Consistently, among its enriched GO terms are “T cell mediated cytotoxicity” and “Cell killing”. This cluster mostly contains cells from decidua samples.
  4. Clusters 10, 14, and 30 are NK cells based on the expression of NKG7 and NCAM1.
    • These clusters are also express GZMA, GZMB, GNLY, PRF1, KLRB1, KLRC1, KLRD1.
    • Cluster 30 is enriched for cell cycle genes, so represents proliferating cells. Vento and surya datasets also have a cluster of proliferating NK cells (dNK.p in vento and NK2 in surya). +
    • Cluster 30 in unambiguously of decidual sample origin. Clusters 10 and 14 are mixed.
annotation$annotation[annotation$cluster %in% c("clust_05")] <- "Tcell_1"
annotation$annotation[annotation$cluster %in% c("clust_09")] <- "Tcell_2"
annotation$annotation[annotation$cluster %in% c("clust_10")] <- "NK_1"
annotation$annotation[annotation$cluster %in% c("clust_14")] <- "NK_2"
annotation$annotation[annotation$cluster %in% c("clust_30")] <- "NK_3"
annotation$annotation[annotation$cluster %in% c("clust_32")] <- "Tcell_3"

annotation$notes[annotation$cluster %in% c("clust_05")] <- "cytotoxic"
annotation$notes[annotation$cluster %in% c("clust_30")] <- "proliferating"


annotation[annotation$cluster %in% paste0("clust_", c("05", "09", "10", "14", "30", "32")), ]

5.7.2 Lymphocyte candidate genes

DotPlot2(features = c("NCAM1", "NKG7", "CD3D", "CD3G", "CD3E", "TRAC", "TRBC1", "TRGC2", "TRDC", "CD8A", "CD8B", "CD4"), title = "Lymphocyte candidate genes")

5.7.3 Marker gene plots

top50marker.plots[["clust_05"]]

top50marker.plots[["clust_09"]]

top50marker.plots[["clust_10"]]

top50marker.plots[["clust_14"]]

top50marker.plots[["clust_30"]]

top50marker.plots[["clust_32"]]

5.7.4 GO enrichment

enrichGO2("clust_05")
1% of input gene IDs are fail to map...
enrichGO2("clust_09")
enrichGO2("clust_10")
1% of input gene IDs are fail to map...
enrichGO2("clust_14")
1% of input gene IDs are fail to map...
enrichGO2("clust_30")
4% of input gene IDs are fail to map...
enrichGO2("clust_32")
1% of input gene IDs are fail to map...

5.7.5 Vento markers

DotPlot2(features = markers.vento$Tcells_2, title = "vento markers: Tcells_2")

DotPlot2(features = markers.vento$Tcells_4, title = "vento markers: Tcells_4")

DotPlot2(features = markers.vento$dNK1, title = "vento markers: dNK1")

DotPlot2(features = markers.vento$dNKp, title = "vento markers: dNKp")

5.7.6 Surya markers

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.NK1"], title = "surya markers: dec.NK1")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.NK2"], title = "surya markers: dec.NK2")

5.8 Myeloid cells

Clusters 01, 03, 08, 11, 12, 22, 26, 27, 28, 31, and 34.

5.8.1 Notes

  1. Clusters 01, 22, 31, 34, and 27 form a group in the hierarchical clustering above, and they all have most cells (more than 75%) coming from villi samples.
    • All five clusters are highly highly correlated with each other, especially clusters 01, 31, and 34.
    • All clusters express various macrophage genes: CD68, CD163, MRC1, MAF, C1QC, C1QA, FOLR2 etc; and more so clusters 01, 31, and 34.
    • Clusters 01 and 31 are most likely villous macrophages (i.e. Hofbauer cells), but the other cluster need more investigation.
    • Clusters 22 and 27, even though correlated with 01 and 34, have marker genes that are not that highly expressed in themselves but are in 01 and 34. Looking at the UMAP plot below, it’s clear that both of these clusters are not as homogenous as the other. The cells are spread between the big myeloid blob and the big fibroblast blob.
    • However, these don’t appear to be clusters of different types of cells. That is, fibroblast genes (COL3A1, COL3A3) and macrophage genes are coexpressed in the same cells in cluster 27 (see plot below). And macrophage genes are broadly expressed in cluster 22, even in cells that seem to be embedded with fibroblast cells. Clusters 22 and 27 might represent cell types that are macrophages but with certain fibroblast-like properties.
    • Looking at cluster 34 markers genes, it interestingly expresses many erythrocyte lineage genes (HBA1, HBA2, HBM, CD1, HBG1, CD235a/GYPA) but it also expresses macrophage genes like CSF1R, FOLR2, MRC1, CD163 etc. If we plot the expression of these genes on UMAP (plotting only clust_34, see below), we see that majority of the cells express macrophage genes, while the erythrocyte genes are expressed in a small number of cells (except HBG2), suggesting that this cluster is made of two cell types. Since majority of the cells in this cluster are macrophage cells, we will label it as such, but should the need arise in further analysis, we can subcluster it further to separate the two cell types out.
  2. Cluster 26 is most likely of erythrocytes (or some cell type from the erythrocyte lineage).
    • The surya dataset does contain what they call an erythroblast cluster, but in erythropoeisis is expected in the placenta in the first trimester. In our term samples, I am not sure if erythrocyte progenitors are expected to be present.
    • Marker genes, similarity with surya EB cluster, and GO enrichement (which include “erythrocyte development”, “oxygen transport”, etc) are all consistent with each other.
    • Most of the cells in this cluster come from villi samples, so fetal origin.
  3. Cluster 28 most likely is made of typical granulocytes.
    • It expresses the granulocyte marker gene CEACAM8, in addition to CEACAM6, ELANE, and MPO.
    • GO enrichment is consistent.
    • All marker genes are consistent: LYZ, DEFA3, DEFA4, S100A8, NCF1, etc.
  4. Cluster 11 and 12 are most likely APC.
    • Of maternal origin. Cluster 12 mostly comes from decidua sample, cluster 11 has mixed contribution, in terms of its gene expression it’s very similar to cluster 12.
    • Very similar to each other. Among vento cluster, most similarity to macrophage clusters. Among surya clusters, most similarity to maternal DC and maternal APC clusters.
    • Expression of classical macrophage/DC markers like CD68, CD163. In addition most of the marker genes of this cluster have to do with antigen presentation. Many of them are MHC-II genes.
    • Not sure if they are macrophages or DCs, but to avoid committing to one conclusion over the other, we can just label them as APCs.
  5. Clusters 3 and 8 are difficult.
    • Low, if any, expression of CD68 and CD163, thus not quite macrophages. But at least cluster 8 is quite similar to macrophages in overall gene expression (see hierarchical clustering above).
    • Low, if any, expression of HLA genes, thus not quite APCs.
    • No expression of typical granulocyte markers: CEACAM8, CEACAM6, MPO, ELANE. But many markers of these clusters sound “granulocytic”. This is evident in GO enrichment as well. In addition, cluster 3 is most similar to cluster 28 (see hierarchical clustering above) which is clearly a granulocyte cluster.
    • Cluster 08 is most similar to the MO_1 and MO_2 clusters from vento. Cluster 3 had low similarity with any of the clusters from vento and surya.
    • Both clusters express MNDA, which is typically only expressed in cells in the monocyte-granulocyte lineage.
    • Both clusters express LYZ, S100A9, S100A8, which I think are also monocyte genes.
    • These might be monocytes but they don’t express CD14.
    • It’s in fact possible to have CD14–ve monocyte, it turns out (Mukherjee et al. 2015). See introduction of (Sampath et al. 2018), which summarizes observations from (Villani et al. 2017). These papers point to non-CD14 monocyte populations. In fact most genes in the Mono_3 cluster of (Villani et al. 2017) are expressed in cluster 03, e.g. MXD1, VNN2, CXCR2.
    • Overall it looks like clusters 3 and 8 are two types of monocytes. (at the very least they are from the monocyte-granulocyte lineage.)
annotation$annotation[annotation$cluster %in% c("clust_01")] <- "vil.Hofb_1"
annotation$annotation[annotation$cluster %in% c("clust_22")] <- "vil.Hofb_2"
annotation$annotation[annotation$cluster %in% c("clust_27")] <- "vil.Hofb_3"
annotation$annotation[annotation$cluster %in% c("clust_31")] <- "vil.Hofb_4"
annotation$annotation[annotation$cluster %in% c("clust_34")] <- "vil.Hofb_5"

annotation$annotation[annotation$cluster %in% c("clust_03")] <- "Mono_1"
annotation$annotation[annotation$cluster %in% c("clust_08")] <- "Mono_2"
annotation$annotation[annotation$cluster %in% c("clust_11")] <- "APC_1"
annotation$annotation[annotation$cluster %in% c("clust_12")] <- "APC_2"
annotation$annotation[annotation$cluster %in% c("clust_26")] <- "vil.Ery"
annotation$annotation[annotation$cluster %in% c("clust_28")] <- "Gran"

annotation$notes[annotation$cluster %in% c("clust_27")] <- "also fibroblast signature"
annotation$notes[annotation$cluster %in% c("clust_34")] <- "likely also contains erythro"
annotation$notes[annotation$cluster %in% c("clust_03")] <- "unsure"
annotation$notes[annotation$cluster %in% c("clust_08")] <- "unsure"

annotation[annotation$cluster %in% paste0("clust_", 
                                          c("01", "03", "08", "11", "12",
                                            "22", "26", "27", "28", "31", "34")), ]

5.8.2 Myeloid candidate genes

DotPlot2(features = c("PTPRC", # pan
                      "CD33", "CD14", "CD68", "CD163", "CLEC10A", "HLA-DRA", # apc
                      "CEACAM8", "CEACAM6", "MPO", "ELANE", # granulocytes
                      "FCGR3A", # inflammatory monocytes?
                      "GYPA" # erythro
                      ), 
         title = "Myeloid candidate genes")

5.8.3 Hofb clusters on UMAP

DimPlot(seur, 
        cols = c(rep("Grey90", 30), "#aec7e8", "#ff9e4a", "#2ca02c", "#ed665d", "#9467bd"), 
        order = (rev(c("clust_01", "clust_22", "clust_27", "clust_31", "clust_34")))) + 
  theme_few() +
  coord_fixed()

5.8.4 clust_22 markers on UMAP

DefaultAssay(seur) <- "SCT"
p <- FeaturePlot(seur, features = c("CD163", "CSF1R", "C1QA", "MRC1", "CD14", "FOLR2"), 
                 cells = rownames(seur@meta.data)[seur@meta.data$seurat_clusters == "clust_22"],
                 coord.fixed = TRUE, ncol = 3, combine = FALSE) 

p[[1]] + p[[2]] + p[[3]] + p[[4]] + p[[5]] + p[[6]] + 
  plot_layout(ncol = 3) +
  plot_annotation(title = "clust_22") &
  theme_bw() +
  theme(panel.grid.major = element_line(size = 0.25),
        panel.grid.minor = element_blank(),
        legend.key.size = unit(0.75, "line"),
        axis.title = element_text(size = 6),
        plot.title = element_text(size = 10)) 

5.8.5 clust_27 markers on UMAP

DefaultAssay(seur) <- "SCT"
p <- FeaturePlot(seur, features = c("COL3A1", "CD14", "MRC1", "LYVE1", "COL1A1"), cells = rownames(seur@meta.data)[seur@meta.data$seurat_clusters == "clust_27"],
                 coord.fixed = TRUE, ncol = 3) 
p[[1]] + p[[2]] + p[[3]] + p[[4]] + p[[5]] + 
  plot_layout(ncol = 3) +
  plot_annotation(title = "clust_27") &
  theme_bw() +
  theme(panel.grid.major = element_line(size = 0.25),
        panel.grid.minor = element_blank(),
        legend.key.size = unit(0.75, "line"),
        axis.title = element_text(size = 6),
        plot.title = element_text(size = 10)) 

5.8.6 clust_34 markers on UMAP

DefaultAssay(seur) <- "SCT"
p <-  FeaturePlot(seur, 
                  features = c("GYPA", "HBM", "HBG2", "HBG1", "ALAS2", "AHSP", 
                               "MRC1", "CD163", "CSF1R"), 
                  cells = rownames(seur@meta.data)[seur@meta.data$seurat_clusters == "clust_34"],
                  coord.fixed = FALSE, ncol = 3)
p[[1]] + p[[2]] + p[[3]] + p[[4]] + p[[5]] + p [[6]] + p[[7]] + p[[8]] + p[[9]] + 
plot_layout(ncol = 3) +
  plot_annotation(title = "clust_34") &
  theme_bw() +
  theme(panel.grid.major = element_line(size = 0.25),
        panel.grid.minor = element_blank(),
        legend.key.size = unit(0.75, "line"),
        axis.title = element_text(size = 6),
        plot.title = element_text(size = 10)) 

5.8.7 Marker gene plots

top50marker.plots[["clust_01"]]

top50marker.plots[["clust_03"]]

top50marker.plots[["clust_08"]]

top50marker.plots[["clust_11"]]

top50marker.plots[["clust_12"]]

top50marker.plots[["clust_22"]]

top50marker.plots[["clust_26"]]

top50marker.plots[["clust_27"]]

top50marker.plots[["clust_28"]]

top50marker.plots[["clust_31"]]

top50marker.plots[["clust_34"]]

5.8.8 GO enrichment

enrichGO2("clust_01")
2% of input gene IDs are fail to map...
enrichGO2("clust_03")
4% of input gene IDs are fail to map...
enrichGO2("clust_08")
1% of input gene IDs are fail to map...
enrichGO2("clust_11")
enrichGO2("clust_12")
1% of input gene IDs are fail to map...
enrichGO2("clust_22")
5.48% of input gene IDs are fail to map...
enrichGO2("clust_26")
4.05% of input gene IDs are fail to map...
enrichGO2("clust_27")
1% of input gene IDs are fail to map...
enrichGO2("clust_28")
3% of input gene IDs are fail to map...
enrichGO2("clust_31")
2% of input gene IDs are fail to map...
enrichGO2("clust_34")
4% of input gene IDs are fail to map...

5.8.9 Vento markers

DotPlot2(features = markers.vento$dM1, title = "vento markers: dM1")

DotPlot2(features = markers.vento$dM2, title = "vento markers: dM2")

DotPlot2(features = markers.vento$HB, title = "vento markers: HB")

DotPlot2(features = markers.vento$M3, title = "vento markers: M3")

DotPlot2(features = markers.vento$MO_1, title = "vento markers: MO_1")

DotPlot2(features = markers.vento$MO_2, title = "vento markers: MO_2")

DotPlot2(features = markers.vento$DC1, title = "vento markers: DC1")

DotPlot2(features = markers.vento$DC2, title = "vento markers: DC2")

5.8.10 Surya markers

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.HC"], title = "surya markers: vil.HC")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.EB"], title = "surya markers: vil.EB")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "dec.APC"], title = "surya markers: dec.APC")

DotPlot2(features = markers.surya$gene[markers.surya$cluster == "vil.HC"], title = "surya markers: vil.HC")

6 Final annotations

6.1 Merge similar clusters

In addition to the annotations so far, we can have a second set of coarser-grained annotations so that we have fewer clusters to deal with. For this we will merge some similar clusters—mostly non-immune clusters, leaving all immune clusters intact.

annotation$annotation_merged <- annotation$annotation

annotation$annotation_merged[annotation$annotation %in% paste0("vil.Hofb_", c(1:5))] <- "vil.Hofb"
annotation$annotation_merged[annotation$annotation %in% paste0("dec.DSC_", c(1:2))] <- "dec.DSC"
annotation$annotation_merged[annotation$annotation %in% paste0("dec.FB_", c(1:3))] <- "dec.FB"
annotation$annotation_merged[annotation$annotation %in% paste0("vil.FB_", c(1:3))] <- "vil.FB"
annotation$annotation_merged[annotation$annotation %in% paste0("APC_", c(1:2))] <- "APC"
annotation$annotation_merged[annotation$annotation %in% paste0("vil.VCT_", c(1:4))] <- "vil.VCT"
annotation$annotation_merged[annotation$annotation %in% paste0("vil.SCT_", c(1:2))] <- "vil.SCT"
annotation$annotation_merged[annotation$annotation %in% c("dec.Endo", "dec.Endo.L")] <- "dec.Endo"
# rearrange columns and write annotations to csv
annotation <- annotation[, c("cluster", "annotation", "annotation_merged", "notes")]

write.csv(annotation, "../results/02_annotation/files/annotations.csv", row.names = FALSE)
annotation %>% kable(caption = "Final annotations of all clusters") %>% kable_styling(full_width = FALSE)
Final annotations of all clusters
cluster annotation notes annotation_merged
clust_00 dec.DSC_1 NA dec.DSC
clust_01 vil.Hofb_1 NA vil.Hofb
clust_02 dec.FB_1 NA dec.FB
clust_03 Mono_1 unsure Mono_1
clust_04 dec.Endo.L NA dec.Endo
clust_05 Tcell_1 cytotoxic Tcell_1
clust_06 vil.FB_1 NA vil.FB
clust_07 vil.FB_2 NA vil.FB
clust_08 Mono_2 unsure Mono_2
clust_09 Tcell_2 NA Tcell_2
clust_10 NK_1 NA NK_1
clust_11 APC_1 NA APC
clust_12 APC_2 NA APC
clust_13 vil.EVT NA vil.EVT
clust_14 NK_2 NA NK_2
clust_15 dec.FB_2 NA dec.FB
clust_16 vil.VCT_1 NA vil.VCT
clust_17 vil.VCT_2 NA vil.VCT
clust_18 dec.DSC_2 NA dec.DSC
clust_19 dec.SMC NA dec.SMC
clust_20 Bcell NA Bcell
clust_21 dec.Endo NA dec.Endo
clust_22 vil.Hofb_2 NA vil.Hofb
clust_23 vil.VCT_3 NA vil.VCT
clust_24 vil.SCT_1 NA vil.SCT
clust_25 vil.VCT_4 proliferating vil.VCT
clust_26 vil.Ery NA vil.Ery
clust_27 vil.Hofb_3 also fibroblast signature vil.Hofb
clust_28 Gran NA Gran
clust_29 vil.SCT_2 NA vil.SCT
clust_30 NK_3 proliferating NK_3
clust_31 vil.Hofb_4 NA vil.Hofb
clust_32 Tcell_3 NA Tcell_3
clust_33 vil.FB_3 NA vil.FB
clust_34 vil.Hofb_5 likely also contains erythro vil.Hofb

6.2 Add annotations to Seurat object

# add annotations
seur@meta.data$annotation <- annotation$annotation[match(
  x = seur@meta.data$seurat_clusters,
  table = annotation$cluster)
  ]

seur@meta.data$annotation_merged <- annotation$annotation_merged[match(
  x = seur@meta.data$seurat_clusters, 
  table = annotation$cluster)
  ]
# write seurat object
saveRDS(seur, file = "../data/seurat-object_annotated.rds")

6.3 Plots

6.3.1 UMAP: all annotations

Idents(seur) <- seur@meta.data$annotation
DimPlot(seur, label = TRUE, repel = TRUE, label.size = 4, shuffle = TRUE) +
  coord_fixed() +
  theme_minimal() +
  theme(legend.position = "none",
        panel.grid = element_blank(),
        axis.title = element_text(size = 8))
Using `as.character()` on a quosure is deprecated as of rlang 0.3.0.
Please use `as_label()` or `as_name()` instead.
This warning is displayed once per session.

ggsave(last_plot(), filename = "../results/02_annotation/plots/umap_annotated.pdf", 
       device = "pdf", width = 7, height = 7, units = "in")

6.3.2 UMAP: merged annotations

Idents(seur) <- seur@meta.data$annotation_merged
DimPlot(seur, label = TRUE, repel = TRUE, label.size = 4, shuffle = TRUE) +
  coord_fixed() +
  theme_minimal() +
  theme(legend.position = "none",
        panel.grid = element_blank(),
        axis.title = element_text(size = 8))


ggsave(last_plot(), filename = "../results/02_annotation/plots/umap_annotated_merged.pdf", 
       device = "pdf", width = 7, height = 7, units = "in")

7 Session Info

sessionInfo()
R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Mojave 10.14.6

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] parallel  stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] org.Hs.eg.db_3.11.4    AnnotationDbi_1.50.3   IRanges_2.22.2         S4Vectors_0.26.1       Biobase_2.48.0        
 [6] BiocGenerics_0.34.0    clusterProfiler_3.16.1 ggthemes_4.2.4         patchwork_1.1.1        kableExtra_1.3.1      
[11] Seurat_3.2.3           forcats_0.5.0          stringr_1.4.0          dplyr_1.0.3            purrr_0.3.4           
[16] readr_1.4.0            tidyr_1.1.2            tibble_3.0.5           ggplot2_3.3.3          tidyverse_1.3.0       

loaded via a namespace (and not attached):
  [1] reticulate_1.18       tidyselect_1.1.0      RSQLite_2.2.2         htmlwidgets_1.5.3     grid_4.0.2           
  [6] BiocParallel_1.22.0   Rtsne_0.15            scatterpie_0.1.5      munsell_0.5.0         codetools_0.2-18     
 [11] ica_1.0-2             future_1.21.0         miniUI_0.1.1.1        withr_2.4.0           colorspace_2.0-0     
 [16] GOSemSim_2.14.2       highr_0.8             knitr_1.30            rstudioapi_0.13       ROCR_1.0-11          
 [21] tensor_1.5            DOSE_3.14.0           listenv_0.8.0         labeling_0.4.2        urltools_1.7.3       
 [26] polyclip_1.10-0       bit64_4.0.5           farver_2.0.3          downloader_0.4        parallelly_1.23.0    
 [31] vctrs_0.3.6           generics_0.1.0        xfun_0.20             R6_2.5.0              graphlayouts_0.7.1   
 [36] rsvd_1.0.3            spatstat.utils_1.20-2 fgsea_1.14.0          gridGraphics_0.5-1    assertthat_0.2.1     
 [41] promises_1.1.1        scales_1.1.1          ggraph_2.0.4          enrichplot_1.8.1      gtable_0.3.0         
 [46] globals_0.14.0        goftest_1.2-2         tidygraph_1.2.0       rlang_0.4.10          splines_4.0.2        
 [51] lazyeval_0.2.2        broom_0.7.3           europepmc_0.4         BiocManager_1.30.10   yaml_2.2.1           
 [56] reshape2_1.4.4        abind_1.4-5           modelr_0.1.8          backports_1.2.1       httpuv_1.5.5         
 [61] qvalue_2.20.0         tools_4.0.2           ggplotify_0.0.5       ellipsis_0.3.1        RColorBrewer_1.1-2   
 [66] ggridges_0.5.3        Rcpp_1.0.6            plyr_1.8.6            progress_1.2.2        prettyunits_1.1.1    
 [71] rpart_4.1-15          deldir_0.2-9          pbapply_1.4-3         viridis_0.5.1         cowplot_1.1.1        
 [76] zoo_1.8-8             haven_2.3.1           ggrepel_0.9.1         cluster_2.1.0         fs_1.5.0             
 [81] magrittr_2.0.1        data.table_1.13.6     scattermore_0.7       DO.db_2.9             lmtest_0.9-38        
 [86] triebeard_0.3.0       reprex_0.3.0          RANN_2.6.1            fitdistrplus_1.1-3    matrixStats_0.57.0   
 [91] hms_1.0.0             mime_0.9              evaluate_0.14         xtable_1.8-4          readxl_1.3.1         
 [96] gridExtra_2.3         compiler_4.0.2        KernSmooth_2.23-18    crayon_1.3.4          htmltools_0.5.1      
[101] mgcv_1.8-33           later_1.1.0.1         lubridate_1.7.9.2     DBI_1.1.1             tweenr_1.0.1         
[106] dbplyr_2.0.0          MASS_7.3-53           Matrix_1.3-2          cli_2.2.0             igraph_1.2.6         
[111] pkgconfig_2.0.3       rvcheck_0.1.8         plotly_4.9.3          xml2_1.3.2            webshot_0.5.2        
[116] rematch_1.0.1         rvest_0.3.6           digest_0.6.27         sctransform_0.3.2     RcppAnnoy_0.0.18     
[121] spatstat.data_1.7-0   rmarkdown_2.6         cellranger_1.1.0      leiden_0.3.6          fastmatch_1.1-0      
[126] uwot_0.1.10           shiny_1.5.0           lifecycle_0.2.0       nlme_3.1-151          jsonlite_1.7.2       
[131] viridisLite_0.3.0     fansi_0.4.2           pillar_1.4.7          lattice_0.20-41       fastmap_1.0.1        
[136] httr_1.4.2            survival_3.2-7        GO.db_3.11.4          glue_1.4.2            spatstat_1.64-1      
[141] png_0.1-7             bit_4.0.4             ggforce_0.3.2         stringi_1.5.3         blob_1.2.1           
[146] memoise_1.1.0         irlba_2.3.3           future.apply_1.7.0   

8 References

Liu, Y., X. Fan, R. Wang, X. Lu, Y-L. Dang, H. Wang, H-Y. Lin, et al. 2018. “Single-Cell RNA-Seq Reveals the Diversity of Trophoblast Subtypes and Patterns of Differentiation in the Human Placenta.” Cell Research 28 (8): 819–32. https://doi.org/10.1038/s41422-018-0066-y.

Mukherjee, R., P. Kanti Barman, P. Kumar Thatoi, R. Tripathy, B. Kumar Das, and B. Ravindran. 2015. “Non-Classical Monocytes Display Inflammatory Features: Validation in Sepsis and Systemic Lupus Erythematous.” Scientific Reports 5 (1): 13886.

Pavličev, M., G. P. Wagner, A. R. Chavan, K. Owens, J. Maziarz, C. Dunn-Fletcher, S. G. Kallapur, L. Muglia, and H. Jones. 2017. “Single-Cell Transcriptomics of the Human Placenta: Inferring the Cell Communication Network of the Maternal-Fetal Interface.” Genome Research. https://doi.org/10.1101/gr.207597.116.

Pizzolato, G., H. Kaminski, M. Tosolini, D-M. Franchini, F. Pont, F. Martins, C. Valle, et al. 2019. “Single-Cell Rna Sequencing Unveils the Shared and the Distinct Cytotoxic Hallmarks of Human Tcrvδ1 and Tcrvδ2 γδ T Lymphocytes.” Proceedings of the National Academy of Sciences 116 (24): 11906–15. https://doi.org/10.1073/pnas.1818488116.

Sampath, P., K. Moideen, U. D. Ranganathan, and R. Bethunaickan. 2018. “Monocyte Subsets: Phenotypes and Function in Tuberculosis Infection.” Frontiers in Immunology 9: 1726. https://doi.org/10.3389/fimmu.2018.01726.

Suryawanshi, H., P. Morozov, A. Straus, N. Sahasrabudhe, K. E. A. Max, A. Garzia, M. Kustagi, T. Tuschl, and Z. Williams. 2018. “A Single-Cell Survey of the Human First-Trimester Placenta and Decidua.” Science Advances 4 (10): eaau4788. https://doi.org/10.1126/sciadv.aau4788.

Vento-Tormo, R., M. Efremova, R. A. Botting, M. Y. Turco, M. Vento-Tormo, K. B. Meyer, J-E. Park, et al. 2018. “Single-Cell Reconstruction of the Early Maternal–Fetal Interface in Humans.” Nature 563 (7731): 347–53. https://doi.org/10.1038/s41586-018-0698-6.

Villani, A-C., R. Satija, G. Reynolds, S. Sarkizova, K. Shekhar, J. Fletcher, M. Griesbeck, et al. 2017. “Single-Cell Rna-Seq Reveals New Types of Human Blood Dendritic Cells, Monocytes, and Progenitors.” Science 356 (6335). https://doi.org/10.1126/science.aah4573.

LS0tCnRpdGxlOiAiQW5ub3RhdGlvbiBvZiBjbHVzdGVycyBpbiAxMHggZGF0YSIKYXV0aG9yOiAiQXJ1biBDaGF2YW4iCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKYmlibGlvZ3JhcGh5OiAuLi9yZWZzLmJpYgotLS0KU3RhcnRlZDogMjAyMC0wOS0wMiAgCkxhc3QgZWRpdGVkOiBgciBmb3JtYXQoU3lzLnRpbWUoKSlgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyMgcGFja2FnZXMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmxpYnJhcnkodGlkeXZlcnNlKQoKIyBzaW5nbGUgY2VsbApsaWJyYXJ5KFNldXJhdCkKCiMgcm1kCmxpYnJhcnkoa2FibGVFeHRyYSkKCiMgcGxvdHRpbmcKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZ2d0aGVtZXMpCgojIGdvIGVucmljaG1lbnQKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpgYGAKCldlIGNhbiB0YWtlIGEgZmlyc3QgcGFzcyBhdCBhbm5vdGF0aW5nIHRoZSBjbHVzdGVycyBmcm9tIG91ciAxMHggZGF0YSBieSBjb21wYXJpbmcgdGhlbSB0byB0aGUgcmVmZXJlbmNlIGRhdGFzZXQgdGhhdCB3ZSBwdXQgdG9nZXRoZXIuIFRoaXMgd2FzIGRvbmUgYnkgY29tcGlsaW5nIGNlbGx0eXBlLWF2ZXJhZ2VkIGV4cHJlc3Npb24gdmFsdWVzIGZyb20gdGhlIGZvbGxvd2luZyBzaW5nbGUgY2VsbCBSTkEtc2VxIHN0dWRpZXM6IFtAcGF2bGljZXZfc2luZ2xlLWNlbGxfMjAxNzsgQHZlbnRvLXRvcm1vX3NpbmdsZS1jZWxsXzIwMTg7IEBzdXJ5YXdhbnNoaV9zaW5nbGUtY2VsbF8yMDE4XS4gCgojIERhdGEKCiMjIFNhbXBsZXMKVGhpcyBpcyB0aGUgc2FtcGxlIGluZm9ybWF0aW9uIHNlbnQgYnkgQWxpY2UuIE5vdCBzdXJlIGFib3V0IHRoZSBJTlAgaWQgZm9yIGBBTDA5YCBhbmQgYEFMMTBgLiBUaGUgbGlicmFyeSBmb3IgYElOUDE4OGAgdmlsbGkgKGFzc29jaWF0ZWQgd2l0aCBgQUwwN2ApIHdhcyBwcm9ibGVtYXRpYywgc28gaXMgbm90IHVzZWQuICAKCmBgYHtyfQpzYW1wbGUuaW5mbyA8LSByZWFkX2NzdigiLi4vZXh0L2Zyb20tYWxpY2Uvc2FtcGxlX2luZm8uY3N2IikKc2FtcGxlLmluZm8gJT4lIGthYmxlKGNhcHRpb24gPSAiU2FtcGxlIGluZm9ybWF0aW9uIikgJT4lIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKCiMjIFNldXJhdC1wcm9jZXNzZWQgZGF0YQpSZWFkIHRoZSBgU2V1cmF0YC1wcm9jZXNzZWQgKGJ5IEVyaWMpIGRhdGEgc2VudCBieSBBbGljZS4gIAoKYGBge3J9CnNldXIgPC0gcmVhZFJEUygiLi4vZXh0L2Zyb20tYWxpY2UvcGxhY2VudGEucmRzIikKCmhlYWQoc2V1ckBtZXRhLmRhdGEpCmBgYAojIyMgQWRkIHNhbXBsZSBtZXRhZGF0YQoKVGhlIGBvcmlnLmlkZW50YCBjb2x1bW4gaW4gdGhlIG1ldGFkYXRhIHJlcHJlc2VudHMgdGhlIHNhbXBsZSBJRHMgZnJvbSB0aGUgc2FtcGxlIGluZm8gdGFibGUgYWJvdmUuIFdlIGp1c3QgaGF2ZSByZW5hbWUgdGhlbSB0byBsZWZ0LXBhZCB0aGUgbnVtYmVycyBmb3IgY29uc2lzdGVuY3ksIGFuZCBzZXQgbmV3IGNsdXN0ZXIgbmFtZXMgYXMgZGVmYXVsdCBpZGVudHMuCgpgYGB7cn0KIyBsZWZ0IHBhZCBvcmlnLmlkZW50cwpzZXVyQG1ldGEuZGF0YSRvcmlnLmlkZW50IDwtIGdzdWIoIihBTCkoXFxkKSQiLCAiXFwxMFxcMiIsIHNldXJAbWV0YS5kYXRhJG9yaWcuaWRlbnQpCgojIGFkZCBhZGRpdGlvbmFsIG1ldGFkYXRhIChjb3ZpZCBzdGF0dXMsIHRpc3N1ZSkKc2V1ckBtZXRhLmRhdGEkY292aWQgPC0gc2FtcGxlLmluZm8kY292aWRbbWF0Y2goeCA9IHNldXJAbWV0YS5kYXRhJG9yaWcuaWRlbnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YWJsZSA9IHNhbXBsZS5pbmZvJEFMX2lkKV0Kc2V1ckBtZXRhLmRhdGEkdGlzc3VlIDwtIHNhbXBsZS5pbmZvJHRpc3N1ZVttYXRjaCh4ID0gc2V1ckBtZXRhLmRhdGEkb3JpZy5pZGVudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YWJsZSA9IHNhbXBsZS5pbmZvJEFMX2lkKV0KCiMgcmVuYW1lIGNsdXN0ZXJzIGZvciBjb25zaXN0ZW5jeQpzZXVyJHNldXJhdF9jbHVzdGVycyA8LSBwYXN0ZTAoImNsdXN0XyIsIHNldXIkc2V1cmF0X2NsdXN0ZXJzKQpzZXVyJHNldXJhdF9jbHVzdGVycyA8LSBnc3ViKCIoY2x1c3RfKShcXGQkKSIsICJcXDEwXFwyIiwgYXMuY2hhcmFjdGVyKHNldXIkc2V1cmF0X2NsdXN0ZXJzKSkgIyBsZWZ0IHBhZApzZXVyJHNldXJhdF9jbHVzdGVycyA8LSBmYWN0b3Ioc2V1ciRzZXVyYXRfY2x1c3RlcnMsIGxldmVscyA9IHBhc3RlMCgiY2x1c3RfIiwgYygiMDAiLCAiMDEiLCAiMDIiLCAiMDMiLCAiMDQiLCAiMDUiLCAiMDYiLCAiMDciLCAiMDgiLCAiMDkiLCAxMDozNCkpKQoKIyBzZXQgc2V1cmF0IGNsdXN0ZXJzIGFzIGRlZmF1bHQgaWRlbnRzCklkZW50cyhzZXVyKSA8LSBzZXVyQG1ldGEuZGF0YSRzZXVyYXRfY2x1c3RlcnMKYGBgCgpgYGB7cn0KIyBhZGQgc2FtcGxlX2lkCnNldXJAbWV0YS5kYXRhJHNhbXBsZV9pZCA8LSBzYW1wbGUuaW5mbyRzYW1wbGVfaWRbbWF0Y2goeCA9IHNldXJAbWV0YS5kYXRhJG9yaWcuaWRlbnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlID0gc2FtcGxlLmluZm8kQUxfaWQpXQpgYGAKCiMjIEF2ZXJhZ2UgYnkgY2x1c3RlcgpXZSBoYXZlIHRvIGdldCB0cmFuc2NyaXB0b21lcyBhdmVyYWdlZCBieSBjbHVzdGVycywgc28gdGhhdCB3ZSBjYW4gY29tcGFyZSB0aGVtIHdpdGggdGhlIHJlZmVyZW5jZSBkYXRhIGluIG9yZGVyIHRvIG5hcnJvdyBkb3duIGFubm90YXRpb25zLiBGb3IgYXZlcmFnaW5nLCB3ZSB3aWxsIHVzZSBgc2N0cmFuc2Zvcm1gIG5vcm1hbGl6ZWQgZGF0YSBzaW5jZSB0aGlzIGlzIHdoYXQgd2UgdXNlIGZvciBtYXJrZXIgZ2VuZSBkZXRlY3Rpb24gZG93biB0aGUgbGluZS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgYXZlcmFnZSBzY3RyYW5zZm9ybWVkIGV4cHJlc3Npb24Kc2V1ci5hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24oc2V1cikKCiMgYXZlcmFnZWQgZGF0YSB0byB1c2UgZm9yIGNvcnJlbGF0aW9ucyAoU0NUIHRyYW5zZm9ybWVkKQpwbGFjIDwtIHNldXIuYXZnJFNDVApgYGAKCiMjIFJlZmVyZW5jZSBkYXRhCmBgYHtyfQojIHJlYWQgcmVmZXJlbmNlIGRhdGFzZXRzCnJlZiA8LSByZWFkLmNzdigiLi4vcmVzdWx0cy8wMV9yZWZlcmVuY2UtYXRsYXMvdmVudG8tc3VyeWEtcGF2bGlfam9pbmVkLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKYGBgCgojIyBHZXQgZGF0YSBpbiBzaGFwZQpXZSB3aWxsIGpvaW4gdGhlIGRhdGEgd2l0aCB0aGUgcmVmZXJlbmNlIGRhdGEgc28gdGhleSBhcmUgYm90aCBpbiB0aGUgc2FtZSBkYXRhZnJhbWUuIAoKYGBge3J9CiMgYWRkIGdlbmUgbmFtZXMgY29sdW1uCnBsYWMkZ2VuZV9uYW1lIDwtIHJvd25hbWVzKHBsYWMpCgojIGlubmVyIGpvaW4KcGxhYyA8LSBkcGx5cjo6aW5uZXJfam9pbihwbGFjLCByZWYsIGJ5ID0gImdlbmVfbmFtZSIpCgpoZWFkKHBsYWMpCmBgYAoKIyBDb3JyZWxhdGlvbnMgd2l0aGluIHRoZSBkYXRhCkJlZm9yZSBtb3ZpbmcgdG8gYW5ub3RhdGluZyBjbHVzdGVycyBieSBjb21wYXJpc29uIHdpdGggdGhlIHJlZmVyZW5jZSBkYXRhc2V0LCBmaXJzdCB3ZSB3aWxsIGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSB3aXRoaW4gdGhlIGRhdGFzZXQuCgojIyBDb3JyZWxhdGlvbiBtYXRyaXgKYGBge3J9CiMgYnVpbGQgY29ycmVsYXRpb24gbWF0cml4IGZyb20gZXhwcmVzc2lvbiBkYXRhCmNvci5tYXRyaXggPC0gY29yKHBsYWMgJT4lIGRwbHlyOjpzZWxlY3Qoc3RhcnRzX3dpdGgoImNsdXN0IikpLCBtZXRob2QgPSAnc3BlYXJtYW4nKQoKIyByZW9yZGVyIGNvcnJlbGF0aW9uIG1hdHJpeCBiYXNlZCBvbiBjbHVzdGVyaW5nCmRkIDwtIGFzLmRpc3QoKDEgLSBjb3IubWF0cml4KSkgCmhjIDwtIGhjbHVzdChkZCwgbWV0aG9kID0gJ2NvbXBsZXRlJykKY29ybWF0IDwtIGNvci5tYXRyaXhbaGMkb3JkZXIsIGhjJG9yZGVyXQoKIyBtZWx0IGNvcnJlbGF0aW9uIG1hdHJpeApkYXQgPC0gcmVzaGFwZTI6Om1lbHQoY29ybWF0LCBuYS5ybSA9IFQpCgojIyBwbG90CnAgPC0gZ2dwbG90KGRhdGEgPSBkYXQsIGFlcyhWYXIxLCBWYXIyLCBmaWxsID0gdmFsdWUpKSArCiAgZ2VvbV90aWxlKGNvbG91ciA9ICJ3aGl0ZSIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICd3aGl0ZScsIGhpZ2ggPSAncmVkJywKICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiU3BlYXJtYW5cbkNvcnJlbGF0aW9uIikgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkgKwogIGxhYnModGl0bGUgPSAiQ29ycmVsYXRpb25zIGFtb25nIGNsdXN0ZXJzIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0xLCB2anVzdCA9IDAuNSksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMC4xNSwgdW5pdHMgPSBjKCdsaW5lcycpKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC44LCB1bml0cyA9IGMoJ2xpbmVzJykpKQoKZ2dzYXZlKHAsIGZpbGVuYW1lID0gIi4uL3Jlc3VsdHMvMDJfYW5ub3RhdGlvbi9wbG90cy9jb3JycGxvdF9jbHVzdGVycy5wZGYiLCAKICAgICAgIGRldmljZSA9ICJwZGYiLCB3aWR0aCA9IDcsIGhlaWdodCA9IDYsIHVuaXRzID0gImluIikKCnByaW50KHApCmBgYAoKIyMgSGVpcmFyY2hpY2FsIGNsdXN0ZXJpbmcKYGBge3J9CiMgYnVpbGQgY29ycmVsYXRpb24gbWF0cml4IGZyb20gZXhwcmVzc2lvbiBkYXRhCmNvci5tYXRyaXggPC0gY29yKHBsYWMgJT4lIGRwbHlyOjpzZWxlY3Qoc3RhcnRzX3dpdGgoImNsdXN0IikpLCBtZXRob2QgPSAnc3BlYXJtYW4nKQoKIyByZW9yZGVyIGNvcnJlbGF0aW9uIG1hdHJpeCBiYXNlZCBvbiBjbHVzdGVyaW5nCmRkIDwtIGFzLmRpc3QoKDEgLSBjb3IubWF0cml4KSkgCmhjIDwtIGhjbHVzdChkZCwgbWV0aG9kID0gJ2NvbXBsZXRlJykKCnBkZihmaWxlID0gIi4uL3Jlc3VsdHMvMDJfYW5ub3RhdGlvbi9wbG90cy9oY2x1c3RfY2x1c3RlcnMucGRmIiwgd2lkdGggPSA3LCBoZWlnaHQgPSA1KQpwbG90KGhjLCBjZXggPSAwLjgpCmRldi5vZmYoKQpwbG90KGhjLCBjZXggPSAwLjgpCmBgYAoKVGhlcmUgYXJlIHRocmVlIGJpZyBjbHVzdGVyIGJsb2Nrcywgd2l0aCBzdWJzdHJ1Y3R1cmUgd2l0aGluIHRoZW0uIFRoZSBjbHVzdGVyIGJsb2NrcyBhbHNvIGNvcnJlc3BvbmQgd2VsbCB3aXRoIHRoZSBVTUFQIHBsb3QgdGhhdCB3YXMgc2VudCBieSBBbGljZS4gCgojIEFubm90YXRpb24gYnkgY29ycmVsYXRpb24KTGV0J3Mgbm93IGFjdHVhbGx5IG1lYXN1cmUgY29ycmVsYXRpb25zIGJldHdlZW4gYWxsIGNsdXN0ZXJzIHdpdGggdGhlIHJlZmVyZW5jZSBkYXRhc2V0cy4gV2Ugd2lsbCB0cmVhdCBvdXIgZGF0YSBhcyBxdWVyeSBkYXRhc2V0IGFuZCBmb3IgZWFjaCBjbHVzdGVyIGFzc2lnbiB0b3AgMyBjZWxsIHR5cGVzIGluIGVhY2ggcmVmZXJlbmNlIGRhdGFzZXQuCgpgYGB7cn0KcmVmZGF0YSA8LSBjKCJ2ZW50byIsICJzdXJ5YSIsICJwYXZsaSIpCgojIGJ1aWxkIGNvcnJlbGF0aW9uIG1hdHJpeCBmcm9tIGV4cHJlc3Npb24gZGF0YQpjb3IubWF0cml4IDwtIGNvcihwbGFjWywgbmFtZXMocGxhYylbbmFtZXMocGxhYykgIT0gImdlbmVfbmFtZSJdXSwgCiAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICdzcGVhcm1hbicpCgojIHJlb3JkZXIgY29ycmVsYXRpb24gbWF0cml4IGJhc2VkIG9uIGNsdXN0ZXJpbmcKZGQgPC0gYXMuZGlzdCgoMSAtIGNvci5tYXRyaXgpKQpoYyA8LSBoY2x1c3QoZGQsIG1ldGhvZCA9ICdjb21wbGV0ZScpCmNvci5tYXRyaXggPC0gY29yLm1hdHJpeFtoYyRvcmRlciwgaGMkb3JkZXJdCgojIG1lbHQgY29ycmVsYXRpb24gbWF0cml4CmNvcm1hdCA8LSByZXNoYXBlMjo6bWVsdChjb3IubWF0cml4LCBuYS5ybSA9IFQpCmNvcm1hdCRWYXIxX3NvdXJjZSA8LSBzYXBwbHkoc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKGNvcm1hdCRWYXIxKSwgc3BsaXQgPSAiXyIpLCAiW1siLCAxKQpjb3JtYXQkVmFyMl9zb3VyY2UgPC0gc2FwcGx5KHN0cnNwbGl0KGFzLmNoYXJhY3Rlcihjb3JtYXQkVmFyMiksIHNwbGl0ID0gIl8iKSwgIltbIiwgMSkKCiMgc3Vic2V0CmNvcm1hdCA8LSBjb3JtYXRbd2hpY2goY29ybWF0JFZhcjFfc291cmNlICVpbiUgcmVmZGF0YSAmIAogICAgICAgICAgICAgICAgICAgY29ybWF0JFZhcjJfc291cmNlID09ICJjbHVzdCIpLCBdCgojIHRvcCAzIG1hdGNoZXMgd2l0aCBoaWdoZXN0IGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cwp0b3BtYXRjaCA8LSBkYXRhLmZyYW1lKCAjIGVtcHR5IGRmIHRvIGZpbGwgdG9wIG1hdGNoZXMgZnJvbSB0aGUgbG9vcCBiZWxvdwogIGNsdXN0ZXIgPSB1bmlxdWUoYXMuY2hhcmFjdGVyKGNvcm1hdCRWYXIyKSkgJT4lIHNvcnQoKSwKICB2ZW50by50b3AxID0gTkEsCiAgc3VyeWEudG9wMSA9IE5BLAogIHBhdmxpLnRvcDEgPSBOQSwKICB2ZW50by50b3AyID0gTkEsCiAgc3VyeWEudG9wMiA9IE5BLAogIHBhdmxpLnRvcDIgPSBOQSwKICB2ZW50by50b3AzID0gTkEsCiAgc3VyeWEudG9wMyA9IE5BLAogIHBhdmxpLnRvcDMgPSBOQQopCgpjb3JtYXQkdG9wMyA8LSBOQQoKZm9yKGkgaW4gdW5pcXVlKGNvcm1hdCRWYXIyKSl7CiAgZm9yKGogaW4gcmVmZGF0YSl7CiAgICAjIGlkZW50aWZ5IHRvcDMgbWF0Y2ggaW5kaWNlcwogICAgaW5kIDwtIHdoaWNoKGNvcm1hdCRWYXIyID09IGkgJiBjb3JtYXQkVmFyMV9zb3VyY2UgPT0gaikKICAgIHZhbCA8LSBjb3JtYXQkdmFsdWVbaW5kXQogICAgdG9wM2luZCA8LSBpbmRbb3JkZXIodmFsLCBkZWNyZWFzaW5nID0gVFJVRSlbMTozXV0KICAgIAogICAgIyBhc3NpZ24gbWF0Y2ggcmFua2luZyB0byBjb3JyZWxhdGlvbiBkYXRhIGZvciBwbG90dGluZwogICAgY29ybWF0JHRvcDNbdG9wM2luZFsxXV0gPC0gIjEiCiAgICBjb3JtYXQkdG9wM1t0b3AzaW5kWzJdXSA8LSAiMiIKICAgIGNvcm1hdCR0b3AzW3RvcDNpbmRbM11dIDwtICIzIgogICAgCiAgICAjIGFzc2lnbiB0b3AgbWF0Y2hlcyB0byB0b3BtYXRjaGVzIGRhdGFmcmFtZQogICAgdG9wbWF0Y2hbdG9wbWF0Y2gkY2x1c3RlciA9PSBpLCBwYXN0ZTAoaiwgIi50b3AxIildIDwtIGdzdWIocGFzdGUwKGosICJfIiksICIiLCBhcy5jaGFyYWN0ZXIoY29ybWF0JFZhcjFbdG9wM2luZFsxXV0pKQogICAgdG9wbWF0Y2hbdG9wbWF0Y2gkY2x1c3RlciA9PSBpLCBwYXN0ZTAoaiwgIi50b3AyIildIDwtIGdzdWIocGFzdGUwKGosICJfIiksICIiLCBhcy5jaGFyYWN0ZXIoY29ybWF0JFZhcjFbdG9wM2luZFsyXV0pKQogICAgdG9wbWF0Y2hbdG9wbWF0Y2gkY2x1c3RlciA9PSBpLCBwYXN0ZTAoaiwgIi50b3AzIildIDwtIGdzdWIocGFzdGUwKGosICJfIiksICIiLCBhcy5jaGFyYWN0ZXIoY29ybWF0JFZhcjFbdG9wM2luZFszXV0pKQogIH0KfQoKYGBgCgojIyBQbG90cwoKYGBge3J9CiMgcGxvdHRpbmcgZnVuY3Rpb24KY2x1c3RBbm5vUGxvdCA8LSBmdW5jdGlvbihkYXQsIHF1ZXJ5LCByZWZlcmVuY2UpIHsKICAKICBkYXQgPC0gZGF0W3doaWNoKGRhdCRWYXIyX3NvdXJjZSA9PSBxdWVyeSAmIGRhdCRWYXIxX3NvdXJjZSA9PSByZWZlcmVuY2UpLCBdCiAgCiAgcCA8LSBnZ3Bsb3QoZGF0YSA9IGRhdCwgCiAgICAgICAgICAgICAgYWVzKFZhcjEsIFZhcjIsIGZpbGwgPSB2YWx1ZSkpICsKICAgIGdlb21fdGlsZShjb2xvdXIgPSAid2hpdGUiKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICd3aGl0ZScsIGhpZ2ggPSAncmVkJywKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJTcGVhcm1hblxuQ29ycmVsYXRpb24iKSArCiAgICBnZW9tX3BvaW50KGFlcyhWYXIxLCBWYXIyLCBhbHBoYSA9IHRvcDMpLAogICAgICAgICAgICAgICBzaXplID0gMS41LCBzaGFwZSA9IDE5LCBzdHJva2UgID0gMCkgKwogICAgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcyA9IGMoMSwgMC41LCAwLjI1KSwgCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygxLCAyLCAzKSwKICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gInRvcDMiLCBuYS52YWx1ZSA9IDApICsKICAgIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkgKwogICAgeGxhYigicmVmZXJlbmNlIikgKwogICAgeWxhYigicXVlcnkiKSArCiAgICBsYWJzKGNhcHRpb24gPSAiRm9yIGVhY2ggY2VsbHR5cGUgaW4gcXVlcnksIGJsYWNrIHBvaW50cyByZXByZXNlbnQgdG9wIDMgY2VsbHR5cGVzIGZyb20gcmVmZXJlbmNlIHdpdGggaGlnaGVzdCBjb3JyZWxhdGlvbi4iLAogICAgICAgICB0aXRsZSA9IHBhc3RlMChxdWVyeSwgIiB2cy4gIiwgcmVmZXJlbmNlKSkgKwogICAgdGhlbWVfYncoKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0xLCB2anVzdCA9IDAuNSksCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgICAgYXhpcy50aWNrcy5sZW5ndGggPSB1bml0KDAuMTUsIHVuaXRzID0gYygnbGluZXMnKSksCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSwKICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuOCwgdW5pdHMgPSBjKCdsaW5lcycpKSkKICAKICByZXR1cm4ocCkKfQoKYGBgCgojIyMgQWdhaW5zdCBWZW50by1Ub3JtbyBldCBhbApgYGB7cn0KIyMgQW5ub3RhdGlvbiBwbG90cwphbm5vLnZlbnRvIDwtIGNsdXN0QW5ub1Bsb3QoZGF0ID0gY29ybWF0LCBxdWVyeSA9ICJjbHVzdCIsIHJlZmVyZW5jZSA9ICJ2ZW50byIpCmNvd3Bsb3Q6Omdnc2F2ZTIoYW5uby52ZW50bywgZGV2aWNlID0gInBkZiIsIHdpZHRoID0gNy41LCBoZWlnaHQgPSA2LjUsIHVuaXRzID0gImluIiwKICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9ICIuLi9yZXN1bHRzLzAyX2Fubm90YXRpb24vcGxvdHMvYW5ub3RhdGlvbi1ieS1jb3JyZWxhdGlvbl9yZWYtdmVudG8ucGRmIikKcHJpbnQoYW5uby52ZW50bykKYGBgCgojIyMgQWdhaW5zdCBTdXJ5YXZhbnNoaSBldCBhbApgYGB7cn0KIyMgQW5ub3RhdGlvbiBwbG90cwphbm5vLnN1cnlhIDwtIGNsdXN0QW5ub1Bsb3QoZGF0ID0gY29ybWF0LCBxdWVyeSA9ICJjbHVzdCIsIHJlZmVyZW5jZSA9ICJzdXJ5YSIpCmNvd3Bsb3Q6Omdnc2F2ZTIoYW5uby5zdXJ5YSwgZGV2aWNlID0gInBkZiIsIHdpZHRoID0gNy41LCBoZWlnaHQgPSA2LjUsIHVuaXRzID0gImluIiwKICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9ICIuLi9yZXN1bHRzLzAyX2Fubm90YXRpb24vcGxvdHMvYW5ub3RhdGlvbi1ieS1jb3JyZWxhdGlvbl9yZWYtc3VyeWEucGRmIikKcHJpbnQoYW5uby5zdXJ5YSkKYGBgCgojIyMgQWdhaW5zdCBQYXZsaWNldiBldCBhbApgYGB7ciBmaWcuYXNwPTAuOX0KIyMgQW5ub3RhdGlvbiBwbG90cwphbm5vLnBhdmxpIDwtIGNsdXN0QW5ub1Bsb3QoZGF0ID0gY29ybWF0LCBxdWVyeSA9ICJjbHVzdCIsIHJlZmVyZW5jZSA9ICJwYXZsaSIpCmNvd3Bsb3Q6Omdnc2F2ZTIoYW5uby5wYXZsaSwgZGV2aWNlID0gInBkZiIsIHdpZHRoID0gNywgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iLAogICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gIi4uL3Jlc3VsdHMvMDJfYW5ub3RhdGlvbi9wbG90cy9hbm5vdGF0aW9uLWJ5LWNvcnJlbGF0aW9uX3JlZi1wYXZsaS5wZGYiKQpwcmludChhbm5vLnBhdmxpKQpgYGAKCiMjIFRvcCBtYXRjaGVzCmBgYHtyfQp0b3BtYXRjaCAlPiUga2FibGUoY2FwdGlvbiA9ICJUb3AgMyBtYXRjaGVzIGFnYWluc3QgYWxsIHJlZmVyZW5jZSBkYXRhc2V0cy4iKSAlPiUga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGZvbnRfc2l6ZSA9IDEwKQoKYGBgCgojIyBMYWJlbHMgZm9yIGNsdXN0ZXJzCldlIG5lZWQgdG8gY3JlYXRlIGNvbnNpc3RlbnQgbGFiZWxzIGZvciBhbGwgY2VsbCB0eXBlcyB0aGF0IHdlIGlkZW50aWZ5LiBCZWxvdyBpcyBhIGBsaXN0YCBJIGNyZWF0ZWQgaW4gd2hpY2ggdGhlIHRvcCBsZXZlbCBvYmplY3RzIGFyZSBsYWJlbHMgd2Ugd2lsbCBnaXZlIHRvIHRoZSBjZWxsIHR5cGVzLCB3aXRoaW4gd2hpY2ggY29udGFpbmVkIGFyZSBsaXN0cyBvZiBjb3JyZXNwb25kaW5nIGxhYmVscyBmcm9tIGB2ZW50b2AgYW5kIGBzdXJ5YWAuIFRoZXNlIGFyZSB0ZW50YXRpdmUgbGFiZWxzLiBBZnRlciB0aGUgZmlyc3QgcGFzcywgd2hlbiB3ZSBnbyB0aHJvdWdoIHRoZSBjbHVzdGVycyB3aXRoIGEgZmluZS10b290aGVkIGNvbWIsIHdlIGNhbiBtb2RpZnkgdGhlbSBmdXJ0aGVyLiBGb3IgZXhhbXBsZSwgaWYgd2UgaGF2ZSBtdWx0aXBsZSBjbHVzdGVycyBsYWJlbGVkIGFzIGBkZWMuRFNDYCAoZGVjaWR1YWwgbWFjcm9waGFnZXMpLCBhbmQgaWYgdGhvc2UgY2x1c3RlcnMgYXJlIGRpc3RpbmN0LCB3ZSBjYW4gdGhlbiBicmVhayB1cCB0aGlzIGxhYmVsIGludG8gYGRlYy5EU0MxYCBhbmQgYGRlYy5EU0MyYC4gCgpgYGB7cn0KbGFicyA8LSBsaXN0KCJkZWMuRFNDIiAgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJkUzEiLCAiZFMyIiwgImRTMyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoImRlYy5EU0MiLCAiZGVjLkZCMSIsICJkZWMuRkIyIikpLAogICAgICAgICAgICAgIkRDIiAgICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiREMxIiwgIkRDMiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoImRlYy5EQzEiLCAiZGVjLkRDMiIpKSwKICAgICAgICAgICAgICJNYWMiICAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoImRNMSIsICJkTTIiLCAiZE0zIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygiZGVjLk1BQyIpKSwKICAgICAgICAgICAgICJOSyIgICAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoImROSy5wIiwgImROSzEiLCAiZE5LMiIsICJkTkszIiwgIk5LLkNEMTZuZWciLCAiTksuQ0QxNnBvcyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoImRlYy5OSzEiLCAiZGVjLk5LMiIpKSwKICAgICAgICAgICAgICJkZWMuU01DIiAgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJkUDEiLCAiZFAyIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygiZGVjLlNNQyIpKSwKICAgICAgICAgICAgICJkZWMuRW5kbyIgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJFbmRvLm0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCJkZWMuVkVDIikpLAogICAgICAgICAgICAgImRlYy5FcGkiICAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIkVwaTEiLCAiRXBpMiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoImRlYy5FRUMiKSksCiAgICAgICAgICAgICAiVGNlbGwiICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJUY2VsbHMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCJkZWMuVEMiKSksCiAgICAgICAgICAgICAidmlsLkVuZG8iICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiRW5kby5mIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygidmlsLlZFQyIpKSwKICAgICAgICAgICAgICJ2aWwuRVZUIiAgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJFVlQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN1cnlhLmxhYiIgPSBjKCJ2aWwuRVZUIikpLAogICAgICAgICAgICAgInZpbC5GQiIgICAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoImZGQjEiLCAiZkZCMiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoInZpbC5GQjEiLCAidmlsLkZCMiIsICJ2aWwuRkIzIikpLAogICAgICAgICAgICAgInZpbC5Ib2ZiIiAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIkhCIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygidmlsLkhDIikpLAogICAgICAgICAgICAgInZpbC5TQ1QiICAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIlNDVCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoInZpbC5TQ1QiKSksCiAgICAgICAgICAgICAidmlsLlZDVCIgICAgPSBsaXN0KCJ2ZW50by5sYWIiID0gYygiVkNUIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygidmlsLlZDVCIpKSwKICAgICAgICAgICAgICJHcmFuIiAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIkdyYW51bG9jeXRlcyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoKSksCiAgICAgICAgICAgICAiSUxDIiAgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCJJTEMzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygpKSwKICAgICAgICAgICAgICJNb25vIiAgID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIk1PIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygpKSwKICAgICAgICAgICAgICJQbGFzbWEiID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIlBsYXNtYSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoKSksCiAgICAgICAgICAgICAiRUIiICAgICA9IGxpc3QoInZlbnRvLmxhYiIgPSBjKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdXJ5YS5sYWIiID0gYygidmlsLkVCIikpLAogICAgICAgICAgICAgInVuay5FbmRvLkwiID0gbGlzdCgidmVudG8ubGFiIiA9IGMoIkVuZG8uTCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3VyeWEubGFiIiA9IGMoImRlYy5MRUMiKSkKKQoKYGBgCgojIyBNYXRjaGVzIGNvbnNpc3RlbnQgYmV0d2VlbiByZWZlcmVuY2UgZGF0YXNldHMKVGhlIGZvbGxvd2luZyBjbHVzdGVycyBoYXZlIGNvbnNpc3RlbnQgdG9wMSBtYXRjaCBiZXR3ZWVuIGB2ZW50b2AgYW5kIGBzdXJ5YWAgcmVmZXJlbmNlcywgaS5lLiB0aGV5IGNvcnJlc3BvbmQgdG8gdGhlIHNhbWUgY2VsbCB0eXBlIGNhdGVnb3J5LiBUaGUgYHBhdmxpYCByZWZlcmVuY2UgaXMgdG9vIHVucmVzb2x2ZWQgdG8gYmUgdXNlZCBkaWFnbm9zdGljYWxseSwgc28gd2Ugd2lsbCBpZ25vcmUgaXQgZm9yIG5vdy4gRm9yIHNvbWUgY2VsbCB0eXBlcyBpdCBpcyBjb25maXJtYXRvcnksIHRob3VnaCwgZS5nLiBjbHVzdGVyXzAwIGNvcnJlc3BvbmRzIHRvIERTQyBpbiBhbGwgdGhyZWUgcmVmZXJlbmNlcy4gCgpgYGB7cn0KIyBmaW5kIG91dCB3aGljaCBjbHVzdGVycyBhcmUgY29uc2lzdGVudC4KIyBJZiB2ZW50by50b3AxIGFuZCBzdXJ5YS50b3AxIGFyZSBmb3VuZCBpbiB0aGUgc2FtZSBpdGVtIGluIHRoZSBsYWJzIGxpc3QsIHRoZSBtYXBwaW5nIGlzIGNvbnNpc3RlbnQuCmNvbnNpc3RlbnQgPC0gYyhOQSkKZm9yKGkgaW4gMTpucm93KHRvcG1hdGNoKSl7CiAgY29uc2lzdGVudFtpXSA8LSBncmVwKHRvcG1hdGNoJHZlbnRvLnRvcDFbaV0sIGxhYnMpID09IGdyZXAodG9wbWF0Y2gkc3VyeWEudG9wMVtpXSwgbGFicykKfQoKIyBzdWJzZXQgdG8gaW5jbHVkZSBjb25zaXN0ZW50IHNldAp0b3BtYXRjaC5jb25zIDwtIHRvcG1hdGNoICU+JSAKICBmaWx0ZXIoY29uc2lzdGVudCkgJT4lIAogIGRwbHlyOjpzZWxlY3QoY2x1c3RlciwgdmVudG8udG9wMSwgc3VyeWEudG9wMSkKCiMgYWRkIG91ciB0ZW50YXRpdmUgbGFiZWxzIHRvIHRoZSBjbHVzdGVycwpmb3IoaSBpbiAxOm5yb3codG9wbWF0Y2guY29ucykpewogIHRvcG1hdGNoLmNvbnMkbGFiZWxbaV0gPC0gbmFtZXMobGFicylbZ3JlcCh0b3BtYXRjaC5jb25zJHZlbnRvLnRvcDFbaV0sIGxhYnMpXQp9CgojIHByaW50CnRvcG1hdGNoLmNvbnNbb3JkZXIodG9wbWF0Y2guY29ucyRsYWJlbCksIF0gJT4lIGtuaXRyOjprYWJsZSgpICU+JSBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgojIyBBbWJpZ3VvdXMgY2x1c3RlcnMKVGhlIHRvcDEgbWF0Y2hlcyBmb3Igc29tZSBjbHVzdGVycyB3aXRoIGB2ZW50b2AgYW5kIGBzdXJ5YWAgYXJlIG5vdCB0aGUgc2FtZS4gVGhlIGFyZSB0aGUgY2x1c3RlcnMgdGhhdCB3aWxsIHJlcXVpcmUgYSBtb3JlIGRldGFpbGVkIGxvb2sgdG8gZGV0ZXJtaW5lIHRoZWlyIGlkZW50aWZ5LiAKCmBgYHtyfQojIHN1YnNldCBmb3IgYW1iaWd1b3VzIGNsdXN0ZXJzCnRvcG1hdGNoLmFtYmlnIDwtIHRvcG1hdGNoICU+JSAKICBmaWx0ZXIoIWNvbnNpc3RlbnQpICU+JSAKICBkcGx5cjo6c2VsZWN0KGNsdXN0ZXIsIHZlbnRvLnRvcDEsIHN1cnlhLnRvcDEpCgojIGFkZCBvdXIgbGFiZWxzLgpmb3IoaSBpbiAxOm5yb3codG9wbWF0Y2guYW1iaWcpKXsKICB0b3BtYXRjaC5hbWJpZyRsYWJlbFtpXSA8LSBwYXN0ZTAoCiAgICBuYW1lcyhsYWJzKVtncmVwKHRvcG1hdGNoLmFtYmlnJHZlbnRvLnRvcDFbaV0sIGxhYnMpXSwKICAgICIgb3IgIiwKICAgIG5hbWVzKGxhYnMpW2dyZXAodG9wbWF0Y2guYW1iaWckc3VyeWEudG9wMVtpXSwgbGFicyldCiAgICApCn0KCiMgcHJpbnQKdG9wbWF0Y2guYW1iaWdbb3JkZXIodG9wbWF0Y2guYW1iaWckbGFiZWwpLCBdICU+JSBrYWJsZSgpICU+JSBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgojIEFubm90YXRpb24gcmVmaW5lbWVudApUaGUgYW5ub3RhdGlvbnMgd2UgaGF2ZSBzbyBmYXIgYXJlIG9ubHkgYSBzdGFydGluZyBwb2ludC4gTm93IHdlIGNhbiBsb29rIGF0IGVhY2ggYW5ub3RhdGlvbiBtb3JlIGNhcmVmdWxseSB0byBtYWtlIHN1cmUgdGhhdCBldmVyeXRoaW5nIG1ha2VzIHNlbnNlLgoKIyMgU2FtcGxlIGNvbnRyaWJ1dGlvbiB0byBjbHVzdGVycwpPbmUgb2YgdGhlIHdheXMgaW4gd2hpY2ggd2UgY2FuIHJlZmluZSB0aGUgY2x1c3RlcnMgZnVydGhlciBpcyBieSBrbm93aW5nIHdoaWNoIHRpc3N1ZSB0aGF0IHNhbXBsZSBhcmlzZXMgZnJvbS4gRm9yIGV4YW1wbGUsIGlmIGEgY2x1c3RlciBoYXMgbWFjcm9waGFnZSBnZW5lIHNpZ25hdHVyZSBhbmQgaWYgbW9zdCBjZWxscyBpbiB0aGF0IGNsdXN0ZXIgY29tZSBmcm9tIHZpbGxpIHNhbXBsZXMsIHdlIGNhbiBmdXJ0aGVyIG5hcnJvdyBkb3duIHRoZSBpZGVudGl0eSBvZiB0aGUgY2x1c3RlciB0byBIb2ZiYXVlciBjZWxscy4gV2UgY2FuIGRvIHRoaXMgYnkgc2ltcGx5IGNvdW50aW5nIGZvciBlYWNoIGNsdXN0ZXIgaG93IG1hbnkgY2VsbHMgY29tZSBmcm9tIGRlY2lkdWEgdnMgdmlsbGkgYW5kIGJ5IGNhbGN1bGF0aW5nIHRoZSBwZXJjZW50YWdlLiAKCmBgYHtyIGZpZy5hc3A9MS4yLCBmaWcud2lkdGg9Ni41fQojIGNvdW50IGNlbGxzIGluIGVhY2ggY2x1c3RlciBieSB0aXNzdWUgb2Ygb3JpZ2luCmJ5dGlzc3VlIDwtIHRhYmxlKHNldXIkdGlzc3VlLCBzZXVyJHNldXJhdF9jbHVzdGVycykgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgZHBseXI6OnJlbmFtZSh0aXNzdWUgPSBWYXIxLCBjbHVzdGVyID0gVmFyMiwgZnJlcXVlbmN5ID0gRnJlcSkKCiMgY2FsY3VsYXRlIGZyYWN0aW9uCmZvcihpIGluIDE6bnJvdyhieXRpc3N1ZSkpewogIGJ5dGlzc3VlJGZyYWN0aW9uW2ldIDwtIGJ5dGlzc3VlJGZyZXF1ZW5jeVtpXS8KICAgIHN1bShieXRpc3N1ZSRmcmVxdWVuY3lbYnl0aXNzdWUkY2x1c3RlciA9PSBieXRpc3N1ZSRjbHVzdGVyW2ldXSkKfQoKIyBwbG90CnAuY2VsbHMgPC0gZ2dwbG90KGRhdGEgPSBieXRpc3N1ZSwgYWVzKHggPSBmcmVxdWVuY3ksIHkgPSBjbHVzdGVyKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsID0gdGlzc3VlKSwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gInN0YWNrIikgKwogIGdndGl0bGUoIk51bWJlciBvZiBjZWxscyIpCgpwLmZyYWMgPC0gZ2dwbG90KGRhdGEgPSBieXRpc3N1ZSwgYWVzKHggPSBmcmFjdGlvbiwgeSA9IGNsdXN0ZXIpKSArCiAgZ2VvbV9iYXIoYWVzKGZpbGwgPSB0aXNzdWUpLCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAic3RhY2siKSArCiAgZ2d0aXRsZSgiRnJhY3Rpb24gb2YgY2VsbHMiKQoKcC5jZWxscyArIHAuZnJhYyArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IiwgbnJvdyA9IDIpICsKICBwbG90X2Fubm90YXRpb24oY2FwdGlvbiA9ICJTYW1wbGUgY29udHJpYnV0aW9uIHRvIGNsdXN0ZXJzIikgJgogIGNvb3JkX2ZsaXAoKSAmCiAgc2NhbGVfZmlsbF90YWJsZWF1KHBhbGV0dGUgPSAiQ2xhc3NpYyAxMCBNZWRpdW0iKSAmCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuMjUpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSkpCiAgCgpgYGAKRm9sbG93aW5nIGNsdXN0ZXJzIGhhdmUgbW9yZSB0aGFuIDcwJSBjZWxscyBjb21pbmcgZnJvbSB0aGUgc2FtZSB0aXNzdWUgb2Ygb3JpZ2luLiAKCmBgYHtyfQpieXRpc3N1ZVssIGMoImNsdXN0ZXIiLCAidGlzc3VlIiwgImZyYWN0aW9uIildICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gInRpc3N1ZSIsIHZhbHVlc19mcm9tID0gImZyYWN0aW9uIikgJT4lIAogIGZpbHRlcihkZWNpZHVhID4gMC43MCB8IHZpbGxpID4gMC43MCkgJT4lIAogIGthYmxlKGRpZ2l0cyA9IDIsIHJvdy5uYW1lcyA9IEZBTFNFLCAKICAgICAgICBjYXB0aW9uID0gIkNsdXN0ZXJzIHdpdGggbW9yZSB0aGFuIDcwJSBjZWxscyBmcm9tIG9uZSB0aXNzdWUiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgpGb2xsb3dpbmcgY2x1c3RlcnMgaGF2ZSBhbWJpZ3VvdXMgb3JpZ2luLCBpLmUuIHRoZXkgY29udGFpbiBjZWxscyBmcm9tIGRlY2lkdWEgYW5kIHZpbGxpIHNhbXBsZXMuIAoKYGBge3J9CmJ5dGlzc3VlWywgYygiY2x1c3RlciIsICJ0aXNzdWUiLCAiZnJhY3Rpb24iKV0gJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAidGlzc3VlIiwgdmFsdWVzX2Zyb20gPSAiZnJhY3Rpb24iKSAlPiUgCiAgZmlsdGVyKGRlY2lkdWEgPCAwLjcwICYgdmlsbGkgPCAwLjcwKSAlPiUgCiAga2FibGUoZGlnaXRzID0gMiwgcm93Lm5hbWVzID0gRkFMU0UsIAogICAgICAgIGNhcHRpb24gPSAiQ2x1c3RlcnMgd2l0aCBjZWxscyBmcm9tIGJvdGggdGlzc3VlcyIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKClRoaXMgaXMgbGFyZ2VseSBjb25zaXN0ZW50IHdpdGggdGhlIHB1dGF0aXZlIGFubm90YXRpb25zLiBBbGwgcHV0YXRpdmVseSB0cm9waG9ibGFzdCBjbHVzdGVycyBjb21lIG92ZXJ3aGVsbWluZ2x5IGZyb20gdmlsbGkgc2FtcGxlcy4gVGhlIG9ubHkgZXhjZXB0aW9uIGlzIGV4dHJhdmlsbG91cyB0cm9waG9ibGFzdCBjbHVzdGVyLCB3aGljaCBhcyBleHBlY3RlZCwgY29tZXMgZnJvbSBkZWNpZHVhIHNhbXBsZXMuIEF0IGxlYXN0IGluIHRoZSBzcGVjaWVzIHRoYXQgSSBoYXZlIHdvcmtlZCB3aXRoLCBpdCBpcyBvZnRlbiBkaWZmaWN1bHQgdG8gc2VwYXJhdGUgdGhlIGZldGFsIHRpc3N1ZSBmcm9tIG1hdGVybmFsIHRpc3N1ZS4gVGhlcmVmb3JlLCB3ZSB1c2UgdGhpcyBhbmFseXNpcyBvbmx5IGFzIGEgcm91Z2ggZ3VpZGUsIGkuZS4gd2hlbiB0aGVyZSBpcyBhIGNvbmZsaWN0IGJldHdlZW4gdGhlIHR3bywgdGhlIG1hcmtlciBnZW5lLWJhc2VkIGFubm90YXRpb24gc2hvdWxkIHRha2UgcHJlY2VkZW5jZSBvdmVyIHRpc3N1ZSBvZiBvcmlnaW4gaW4gY2VsbCB0eXBlIGlkIGJlY2F1c2UgdGhlIGxhdHRlciBpcyBleHBlcmltZW50YWxseSAoYW5kIGFzIGEgY29uc2VxdWVuY2UsIGFuYWx5dGljYWxseSkgbWVzc3kgdG8gZGlzZW50YW5nbGUuCgpNb3N0IGNsdXN0ZXJzIHRoYXQgaGF2ZSBtaXhlZCBvcmlnaW4sIGFyZSBwdXRhdGl2ZSBpbW11bmUgY2VsbCB0eXBlIGNsdXN0ZXJzLiBUaGlzIGNvdWxkIHN1Z2dlc3QgdGhhdCB0aGUgaW1tdW5lIGNlbGwgdHlwZXMgYXJlIHByZXNlbnQgb24gYm90aCBtYXRlcm5hbCBhbmQgZmV0YWwgc2lkZXMgb2YgdGhlIGludGVyZmFjZSwgYW5kIGJlY2F1c2UgdGhleSBhcmUgdGhlIHNhbWUgY2VsbCB0eXBlLCB0aGV5IGVuZCB1cCBpbiB0aGUgc2FtZSBjbHVzdGVyLiBUaGlzIG1lYW5zIHRoYXQgd2UgY2FuJ3QgY29uZmlkZW50bHkgc2F5IGZvciBpbW11bmUgY2VsbHMgd2hldGhlciB0aGV5IGFyZSBkZWNpZHVhbCBvciB2aWxsb3VzLiBGb3IgdGhpcyByZWFzb24sIHdoZW4gc3VjaCBhbWJpZ3VpdHkgaXMgcHJlc2VudCwgd2Ugd2lsbCBsYWJlbCB0aGVtIHdpdGhvdXQgdGhlIHRpc3N1ZSBwcmVmaXgsIGkuZS4gZm9yIGV4YW1wbGUgYFRjZWxsYCBpbnN0ZWFkIG9mIGBkZWMuVGNlbGxgIG9yIGB2aWwuVGNlbGxgLgoKIyMgTWFya2VyIGdlbmVzCiMjIyBJZGVudGlmeSBtYXJrZXIgZ2VuZXMgCkkgcmFuIGBTZXVyYXQ6OmZpbmRBbGxNYXJrZXJzYCBvbiB0aGlzIGRhdGEgdXNpbmcgdGhlIGBTQ1RgIGFzc2F5LiBUaGlzIGZ1bmN0aW9uIG91dHB1dHMgYSBsaXN0IG9mIG1hcmtlciBnZW5lcyBmb3IgZWFjaCBjbHVzdGVyIGNvbXBhcmVkIHRvIGFsbCBvdGhlciBjZWxscyBpbiB0aGUgZGF0YS4gVGhpcyB0YWtlcyBhIHdoaWxlIChob3VycykgdG8gcnVuLCBzbyBJIHJhbiBpdCBvbiB0aGUgY2x1c3RlciBhbmQgc2F2ZWQgdGhlIHJlc3VsdHMgdG8gYSBgLmNzdmAuIFNlZSB0aGUgYGNvZGVgIGRpcmVjdG9yeSBmb3IgZnVsbCBjb2RlLCBhbmQgc2VlIGJlbG93IGZvciB0aGUgZnVuY3Rpb24gY2FsbC4gCgpgYGB7ciBlY2hvPVRSVUUsIGV2YWw9RkFMU0V9CiMgc2V0IGRlZmF1bHQgYXNzYXkgdG8gU0NUCkRlZmF1bHRBc3NheShvYmplY3QgPSBwbGFjKSA8LSAiU0NUIgoKIyBmaW5kIG1hcmtlcnMgZm9yIGFsbCBjbHVzdGVycwptYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKG9iamVjdCA9IHBsYWMsIAogICAgICAgICAgICAgICAgICAgICAgICAgIG9ubHkucG9zID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dmYy50aHJlc2hvbGQgPSAwLjI1KSAgCgojIHdyaXRlLmNzdgp3cml0ZS5jc3YobWFya2VycywgIi9ob21lL2FyYzc4L3NjcmF0Y2g2MC9jb3ZpZC1wbGFjZW50YS9tYXJrZXJzX3NjdC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgpgYGB7cn0KbWFya2VycyA8LSByZWFkLmNzdigiLi4vcmVzdWx0cy8wMl9hbm5vdGF0aW9uL2ZpbGVzL21hcmtlcnNfc2N0LmNzdiIpCgojIHJlbmFtZSBjbHVzdGVycwptYXJrZXJzJGNsdXN0ZXIgPC0gcGFzdGUwKCJjbHVzdF8iLCBtYXJrZXJzJGNsdXN0ZXIpCm1hcmtlcnMkY2x1c3RlciA8LSBnc3ViKCIoY2x1c3RfKShcXGQpJCIsICJcXDEwXFwyIiwgbWFya2VycyRjbHVzdGVyKQptYXJrZXJzJGNsdXN0ZXIgPC0gZmFjdG9yKG1hcmtlcnMkY2x1c3RlciwgbGV2ZWxzID0gdW5pcXVlKG1hcmtlcnMkY2x1c3RlcikpCgojIHNwbGl0IGJ5IGNsdXN0ZXIKbWFya2VycyA8LSBzcGxpdC5kYXRhLmZyYW1lKG1hcmtlcnMsIG1hcmtlcnMkY2x1c3RlcikKYGBgCgojIyMgUGxvdCBtYXJrZXIgZ2VuZXMKV2Ugd2lsbCBwbG90IHRvcCA1MCBtYXJrZXIgZ2VuZXMgZm9yIGFsbCBjbHVzdGVycyBhbmQgc2F2ZSB0aGUgcGxvdCB0byBwZGYuIApgYGB7cn0KIyBwbG90dGluZyBmdW5jdGlvbgpEb3RQbG90MiA8LSBmdW5jdGlvbihvYmplY3QgPSBzZXVyLCBhc3NheSA9ICJTQ1QiLCBmZWF0dXJlcywgdGl0bGUsIC4uLikgewogIHAgPC0gRG90UGxvdChvYmplY3QsIAogICAgICAgICAgICAgICBhc3NheSA9IGFzc2F5LAogICAgICAgICAgICAgICBmZWF0dXJlcyA9IGZlYXR1cmVzLCAKICAgICAgICAgICAgICAgZG90Lm1pbiA9IDAuMDUsIGRvdC5zY2FsZSA9IDQpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBsYWJzKGNhcHRpb24gPSBwYXN0ZTAoYXNzYXksICIgbm9ybWFsaXplZCBleHByZXNzaW9uIiksIHRpdGxlID0gdGl0bGUpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSwKICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpCiAgCiAgcmV0dXJuKHApCn0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzIDwtIGxpc3QoKQpmb3IoaSBpbiBuYW1lcyhtYXJrZXJzKSkgewogIGlmKG5yb3cobWFya2Vyc1tbaV1dKSA+IDUwKQogICAgZmVhdHVyZXMgPC0gbWFya2Vyc1tbaV1dJGdlbmVbMTo1MF0KICBpZihucm93KG1hcmtlcnNbW2ldXSkgPCA1MCkKICAgIGZlYXR1cmVzIDwtIG1hcmtlcnNbW2ldXSRnZW5lCgogIHRvcDUwbWFya2VyLnBsb3RzW1tpXV0gPC0gRG90UGxvdDIoZmVhdHVyZXMgPSBmZWF0dXJlcywgdGl0bGUgPSBpKQogICAgICAgICAKICBjb3dwbG90OjpnZ3NhdmUyKHRvcDUwbWFya2VyLnBsb3RzW1tpXV0sIAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoIi4uL3Jlc3VsdHMvMDJfYW5ub3RhdGlvbi9wbG90cy90b3A1MG1hcmtlcnNfZG90cGxvdF8iLCBpLCAiLnBkZiIpLAogICAgICAgICAgICAgICAgICAgd2lkdGggPSA3LjUsIGhlaWdodCA9IDcuNSwgdW5pdHMgPSAiaW4iKQp9CmBgYAoKIyBNYW51YWwgYW5ub3RhdGlvbgpXaXRoIHRoZSBtYXJrZXIgZ2VuZSBwbG90cyB3ZSBoYXZlIG1hZGUsIHdlIGNhbiBleGFtaW5lIHRoZSB0b3AgbWFya2VyIGdlbmVzIGluIGVhY2ggY2x1c3RlciB0byBtYW51YWxseSBjb25maXJtIG9yIGFkanVzdCB0aGUgYW5ub3RhdGlvbiBvZiBlYWNoIGNsdXN0ZXIuCgpMZXQncyBtYWtlIGEgZGF0YWZyYW1lIGluIHdoaWNoIHRvIGtlZXAgdHJhY2sgb2YgdGhlIGFzc2lnbm1lbnRzLgoKYGBge3J9CmFubm90YXRpb24gPC0gZGF0YS5mcmFtZSgKICBjbHVzdGVyID0gdW5pcXVlKHNldXIkc2V1cmF0X2NsdXN0ZXJzKSAlPiUgc29ydCgpLAogIGFubm90YXRpb24gPSBOQSwKICBub3RlcyA9IE5BCikKYGBgCgpGb3IgZWFjaCBzZXQgb2YgbWFya2VyIGdlbmVzLCB3ZSBjYW4gYWxzbyBwZXJmb3JtIGEgbnVtYmVyIG9mIG1hbnVhbCBjaGVja3MgdG8gYXNzZXNzIGNlbGwgdHlwZSBhbm5vdGF0aW9uLiAgCgpGaXJzdCBpcyBHTyBlbnJpY2htZW50IHVzaW5nIGBjbHVzdGVyUHJvZmlsZXJgIHBhY2thZ2UsIGZvciB3aGljaCBiZWxvdyBpcyBhIGZ1bmN0aW9uIGZvciBxdWlja2x5IGxvb2tpbmcgYXQgdG9wIDEwIGVucmljaGVkIEdPIGNhdGVnb3JpZXMuCmBgYHtyfQojIGZ1bmN0aW9uIGZvciBHTyBlbnJpY2htZW50CiMgMS4gZm9yIGEgZ2l2ZW4gY2x1c3RlciwgcHJpbnRzIG4ucHJpbnQucm93cyB0b3AgZW5yaWNoZWQgR08gdGVybXMKIyAyLiBDYW4gZXhjbHVkZSByaWJvc29tYWwgZ2VuZXMgKGRlZmF1bHQpIGJlY2F1c2UgdGhleSBkb21pbmF0ZSBzb21lIEdPIGVucmljaG1lbnRzCmVucmljaEdPMiA8LSBmdW5jdGlvbihjbHVzdCwgbmdlbmVzID0gMTAwLCBpbmNsdWRlLnJpYm8gPSBGQUxTRSwgbi5wcmludC5yb3dzID0gMTAsIC4uLikgewogIAogIGlkcy5iaXRyIDwtIGJpdHIobWFya2Vyc1tbY2x1c3RdXSRnZW5lWzE6bmdlbmVzXSwgCiAgICAgICAgICAgICAgICBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSBjKCJFTlRSRVpJRCIsICJTWU1CT0wiKSwgCiAgICAgICAgICAgICAgICBPcmdEYiA9ICJvcmcuSHMuZWcuZGIiKQogICAgCiAgaWYoaW5jbHVkZS5yaWJvID09IEZBTFNFKQogICAgZmVhdHVyZXMgPC0gaWRzLmJpdHIkRU5UUkVaSURbIShncmVwbCgiUlBbU0xdIiwgaWRzLmJpdHIkU1lNQk9MKSldCiAgaWYoaW5jbHVkZS5yaWJvID09IFRSVUUpCiAgICBmZWF0dXJlcyA8LSBpZHMuYml0ciRFTlRSRVpJRAoKICBlZ28gPC0gZW5yaWNoR08oCiAgICBnZW5lICAgICAgICAgID0gZmVhdHVyZXMsICAgIAogICAgT3JnRGIgICAgICAgICA9IG9yZy5Icy5lZy5kYiwKICAgIG9udCAgICAgICAgICAgPSAiQlAiLAogICAgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgICBwdmFsdWVDdXRvZmYgID0gMC4wMSwgIyBkZWZhdWx0IDAuMDEKICAgIHF2YWx1ZUN1dG9mZiAgPSAwLjA1LCAjIGRlZmF1bHQgMC4wNQogICAgcmVhZGFibGUgPSBUUlVFKQogIAogIGRmdG9wcmludCA8LSBjbHVzdGVyUHJvZmlsZXI6OnNpbXBsaWZ5KGVnbylAcmVzdWx0WzE6bi5wcmludC5yb3dzLCBjKDIsOCldCiAgcm93bmFtZXMoZGZ0b3ByaW50KSA8LSBOVUxMCiAgCiAgcmV0dXJuKGRmdG9wcmludCkKfQpgYGAKCldlIGNhbiBhbHNvIGNoZWNrIGlmIHRoZSBjbHVzdGVyIGlzIGVucmljaGVkIGlzIGdlbmVzIHRoYXQgYXJlIGxhYmVsZWQgYXMgbWFya2VycyBnZW5lcyBmb3IgYSBjZWxsIHR5cGUgYnkgb3RoZXIgc3R1ZGllcy4gW0B2ZW50by10b3Jtb19zaW5nbGUtY2VsbF8yMDE4XSBoYXZlIHByb3ZpZGVkIHRvcCAzMCBtYXJrZXIgZ2VuZXMgZm9yIGFsbCBjZWxsIHR5cGVzIGlkZW50aWZpZWQgYnkgdGhlbS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgcmVhZCB0b3AgMzAgbWFya2VycyBmcm9tIHZlbnRvLXRvcm1vIGV0IGFsCm1hcmtlcnMudmVudG8gPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKCIuLi9leHQvZnJvbS1wYXBlcnMvdmVudG8tdG9ybW9fMjAxOC80MTU4Nl8yMDE4XzY5OF9NT0VTTTFfRVNNL1N1cHBsZW1lbnRhcnkgVGFibGUgMi54bHN4IikKCiMgcmVuYW1lIGNvbHVtbnMKbWFya2Vycy52ZW50byA8LSBkcGx5cjo6cmVuYW1lKG1hcmtlcnMudmVudG8sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWQ1RfMSA9IFZDVC4uLjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUY2VsbHNfMSA9IGBUIGNlbGxzLi4uNWAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUY2VsbHNfMiA9IGBUIGNlbGxzLi4uOWAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWQ1RfMiA9IGBWQ1QuLi4xMGAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUY2VsbHNfMyA9IGBUIGNlbGxzLi4uMTFgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRkJfMSA9IEYxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRVZUXzEgPSBgRVZULi4uMTRgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGNlbGxzXzQgPSBgVCBjZWxscy4uLjE1YCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVWVF8yID0gYEVWVC4uLjE2YCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5LQ0QxNnBvcyA9IGBCbG9vZCBOSyBDRDE2K2AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNT18xID0gYE1PLi4uMjJgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTU9fMiA9IGBNTy4uLjI3YCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5LQ0QxNm5lZyA9IGBCbG9vZCBOSyBDRDE2LWAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGQjIgPSBGMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVuZG8ubSA9IGBFbmRvIChtKWAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFbmRvLkwgPSBgRW5kbyBMYCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVuZG8uZiA9IGBFbmRvIChmKWAgCikKCiMgcmVtb3ZlIGVuc2VtYmwgaWRzCm1hcmtlcnMudmVudG8gPC0gYXBwbHkoWCA9IG1hcmtlcnMudmVudG8sIE1BUkdJTiA9IDIsIEZVTiA9IGZ1bmN0aW9uKHgpIGdzdWIoIl9FTlNHXFxkKyIsICIiLCB4KSkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQpgYGAKCltAc3VyeWF3YW5zaGlfc2luZ2xlLWNlbGxfMjAxOF0gaGF2ZSBhbHNvIHByb3ZpZGVkIGEgbGlzdCBvZiB0b3AzMCBtYXJrZXIgZ2VuZXMgaW4gdGhlIHN1cHBsZW1lbnRhcnkgZGF0YS4gCmBgYHtyfQptYXJrZXJzLnN1cnlhLnZpbCA8LSByZWFkeGw6OnJlYWRfZXhjZWwoIi4uL2V4dC9mcm9tLXBhcGVycy9zdXJ5YXdhbnNoaV8yMDE4L3N1cHAtZGF0YS9hYXU0Nzg4X0RhdGFfZmlsZV9TMy54bHN4Iiwgc2hlZXQgPSAiVmlsbGkiLCByYW5nZSA9ICJBMTpHMjcxIiwgdHJpbV93cyA9IFRSVUUpCm1hcmtlcnMuc3VyeWEuZGVjIDwtIHJlYWR4bDo6cmVhZF9leGNlbCgiLi4vZXh0L2Zyb20tcGFwZXJzL3N1cnlhd2Fuc2hpXzIwMTgvc3VwcC1kYXRhL2FhdTQ3ODhfRGF0YV9maWxlX1MzLnhsc3giLCBzaGVldCA9ICJEZWNpdWRhIiwgcmFuZ2UgPSAiQTE6RzMzMSIsIHRyaW1fd3MgPSBUUlVFKQoKbWFya2Vycy5zdXJ5YS52aWwgPC0gZHBseXI6OnJlbmFtZShtYXJrZXJzLnN1cnlhLnZpbCwgY2x1c3RlciA9IGBjZWxsIHR5cGVgKQoKbWFya2Vycy5zdXJ5YS52aWwkY2x1c3RlciA8LSBwYXN0ZTAoInZpbC4iLCBtYXJrZXJzLnN1cnlhLnZpbCRjbHVzdGVyKQptYXJrZXJzLnN1cnlhLmRlYyRgY2x1c3RlcmAgPC0gcGFzdGUwKCJkZWMuIiwgbWFya2Vycy5zdXJ5YS5kZWMkYGNsdXN0ZXJgKQoKbWFya2Vycy5zdXJ5YSA8LSByYmluZChtYXJrZXJzLnN1cnlhLnZpbCwgbWFya2Vycy5zdXJ5YS5kZWMpCmBgYAoKCiMjIERTQwpDbHVzdGVycyAwIGFuZCAxOC4KCiMjIyBOb3RlcwoxLiBDbHVzdGVycyAwIGFuZCAxOCBoaWdobHkgY29ycmVsYXRlZCB3aXRoIGVhY2ggb3RoZXIgYW5kIG1vc3Qgc2ltaWxhciB0byBlYWNoIG90aGVyIHRoYW4gdG8gb3RoZXIgY2x1c3RlcnMuIAoyLiBCb3RoIGFyZSBjb25zaXN0ZW50bHkgc2ltaWxhciB0byB0aGUgZGVjaWR1YWwgc3Ryb21hbCBjZWxscyBpbiBib3RoIHZlbnRvIGFuZCBzdXJ5YSBkYXRhc2V0cy4gCjMuIFRoZSBtYXJrZXIgZ2VuZXMgaW5jbHVkZSBQcm9sYWN0aW4sIElHRkJQMSBldGMsIHdoaWNoIGFyZSB0eXBpY2FsIG9mIGRlY2lkdWFsIHN0cm9tYWwgY2VsbHMuIAo0LiBUaGVzZSB0d28gY2x1c3RlcnMgYWxzbyBhcmlzZSB1bmFiaWd1b3VzbHkgZnJvbSB0aGUgZGVjaWR1YSBzYW1wbGVzIHJhdGhlciB0aGFuIHZpbGxpIHNhbXBsZXMuCgpgYGB7cn0KYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzAwIildIDwtICJkZWMuRFNDXzEiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8xOCIpXSA8LSAiZGVjLkRTQ18yIgoKYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBwYXN0ZTAoImNsdXN0XyIsIGMoIjAwIiwgIjE4IikpLCBdCmBgYAoKIyMjIE1hcmtlciBnZW5lcyBwbG90cwpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMDAiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMTgiXV0KYGBgCgojIyMgR28gYW5yaWNobWVudApgYGB7cn0KZW5yaWNoR08yKCJjbHVzdF8wMCIpCmBgYAoKIyMjIFZlbnRvIG1hcmtlcnMKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRkUzMsIHRpdGxlID0gInZlbnRvOiBkUzMiKQpgYGAKCiMjIyBTdXJ5YSBtYXJrZXJzCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMuc3VyeWEkZ2VuZVttYXJrZXJzLnN1cnlhJGNsdXN0ZXIgPT0gImRlYy5EU0MiXSwgdGl0bGUgPSAic3VyeWEgbWFya2VyczogRFNDIikKYGBgCgojIyBFbmRvdGhlbGlhbCBjZWxscwpDbHVzdGVycyA0IGFuZCAyMS4KCiMjIyBOb3RlcwoKMS4gQ2x1c3RlcnMgNCBhbmQgMjEgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLiAKMi4gVGhleSBjb25zaXN0ZW50bHkgZ2V0IGFzc2lnbmVkIGFzIGRlY2lkdWFsIGVuZG90aGVsaWFsIGNlbGxzIGJ5IGJvdGggdmVudG8gYW5kIHN1cnlhIGRhdGFzZXRzLgozLiBNb3JlIHRoYW4gNzUlIG9mIHRoZSBjZWxscyBpbiBib3RoIG9mIHRoZXNlIGNsdXN0ZXJzIGNvbWUgZnJvbSB0aGUgZGVjaWR1YSBzYW1wbGVzLgo0LiBNYXJrZXJzIGdlbmVzIG9mIGJvdGggY2x1c3RlcnMgYXJlIGVucmljaGVkIGluIEdPIGNhdGVnb3JpZXMgcmVsYXRlZCB0byBlcGl0aGVsaXVtIGRldmVsb3BtZW50LCBlbmRvdGhlbGl1bSBkZXZlbG9wbWVudCBldGMuIAo1LiBCdXQgdGhlc2UgdHdvIGNsdXN0ZXJzIGFyZSBub3QgYXMgYWxpa2UgYXMgY2x1c3RlcnMgMCBhbmQgMTguIFRoZXkgYXJlIGRpZmZlcmVudCBpbiB0ZXJtcyBvZiB0aGVpciBleHByZXNzaW9uIG9mIG1hbnkgZW5kb3RoZWxpdW0gcmVsYXRlZCBnZW5lcyBsaWtlIENEMzQsIFBST1gxLCBWV0YsIFNQQVJDTC4gQ2x1c3RlciAyMSBsb29rcyBtb3JlIGxpa2UgInR5cGljYWwiIGVuZG90aGVsaWFsIGNlbGxzLiBDbHVzdGVyIDQgaGFzIGxvdyBleHByZXNzaW9uIG9mIG1hbnkgdHlwaWNhbCBlbmRvdGhlbGlhbCBnZW5lcy4gCjYuIENsdXN0ZXIgNCBoYXMgaGlnaGVyIGV4cHJlc3Npb24gb2Ygc29tZSBseW1waGF0aWMgZW5kb3RoZWxpYWwgY2VsbHM6IFBST1gxLCBMWVZFMS4gTWFueSBnZW5lcyBmcm9tIHRoZSBsaXN0IG9mIHN1cnlhIG1hcmtlciBnZW5lcyBmb3IgZGVjLkxFQyBhcmUgZXhwcmVzc2VkIGluIGNsdXN0ZXIgNC4gVGhpcyBzdWdnZXN0cyB0aGF0IENsdXN0ZXIgNCBtYXkgYmUgbHltcGhhdGljIGVuZG90aGVsaWFsIGNlbGxzIGFuZCBjbHVzdGVyIDIxIG1heSBiZSBlbmRvdGhlbGlhbCBjZWxscy4gCgpgYGB7cn0KYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzA0IildIDwtICJkZWMuRW5kby5MIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMjEiKV0gPC0gImRlYy5FbmRvIgoKYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBwYXN0ZTAoImNsdXN0XyIsIGMoIjA0IiwgIjIxIikpLCBdCmBgYAoKIyMjIE1hcmtlciBnZW5lIHBsb3RzCmBgYHtyLCBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMDQiXV0KYGBgCgpgYGB7ciwgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90c1tbImNsdXN0XzIxIl1dCmBgYAoKIyMjIEdPIGVucmljaG1lbnQKYGBge3J9CmVucmljaEdPMigiY2x1c3RfMDQiKQpgYGAKCmBgYHtyfQplbnJpY2hHTzIoImNsdXN0XzIxIikKYGBgCgojIyMgVmVudG8gbWFya2VycwpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJEVuZG8ubSwgdGl0bGUgPSAidmVudG86IEVuZG8ubSIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRFbmRvLmYsIHRpdGxlID0gInZlbnRvOiBFbmRvLmYiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMudmVudG8kRW5kby5MLCB0aXRsZSA9ICJ2ZW50bzogRW5kby5MIikKYGBgCgojIyMgU3VyeWEgbWFya2VycwpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJ2aWwuVkVDIl0sIHRpdGxlID0gInN1cnlhIG1hcmtlcnM6IHZpbC5WRUMiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMuc3VyeWEkZ2VuZVttYXJrZXJzLnN1cnlhJGNsdXN0ZXIgPT0gImRlYy5WRUMiXSwgdGl0bGUgPSAic3VyeWEgbWFya2VyczogZGVjLlZFQyIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy5zdXJ5YSRnZW5lW21hcmtlcnMuc3VyeWEkY2x1c3RlciA9PSAiZGVjLkxFQyJdLCB0aXRsZSA9ICJzdXJ5YSBtYXJrZXJzOiBkZWMuTEVDIikKYGBgCgojIyBFVlQKQ2x1c3RlciAxMy4gCgojIyMgTm90ZXMKCjEuIFRoaXMgY2x1c3RlciBpcyB1bmlxdWUgaW4gaXRzIGV4cHJlc3Npb24gb2YgSExBLUcsIHR5cGljYWwgb2YgZXh0cmF2aWxsb3VzIHRyb3Bob2JsYXN0LiAKMi4gU2V2ZXJhbCBvdGhlciBnZW5lcyBvZnRlbiBlbnJpY2hlZCBpbiBFVlQgYXJlIGFsc28gZW5yaWNoZWQgaW4gdGhpcyBjbHVzdGVyOiBGTjEsIFBBUFBBMiwgRElPMiwgZXRjLgozLiB0b3AgMzAgbWFya2VycyBhc3NvY2lhdGVkIHdpdGggRVZUXzIgZnJvbSB2ZW50byBkYXRhc2V0IGFyZSBhbHNvIGhpZ2hseSBhbmQgc3BlY2lmaWNhbGx5IGV4cHJlc3NlZCBpbiB0aGlzIGNsdXN0ZXIuIAo0LiBJdCBpcyBjb25zaXN0ZW50bHkgYXNzaWduZWQgdG8gYmUgRVZUIGJhc2VkIG9uIHZlbnRvLCBzdXJ5YSwgYW5kIHBhdmxpIGRhdGFzZXRzLiAKCkFubm90YXRpb246ICAKKipDbHVzdGVyIDEzOiBFeHRyYXZpbGxvdXMgdHJvcGhvYmxhc3QgKHZpbC5FVlQpKioKCmBgYHtyfQphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMTMiKV0gPC0gInZpbC5FVlQiCgphbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzEzIiksIF0KYGBgCgojIyMgTWFya2VyIGdlbmVzIHBsb3QKYGBge3IgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90c1tbImNsdXN0XzEzIl1dCmBgYAoKIyMjIEdPIGVucmljaG1lbnQKYGBge3IgbWVzc2FnZT1GQUxTRX0KZW5yaWNoR08yKCJjbHVzdF8xMyIpCmBgYAoKIyMjIFZlbnRvIG1hcmtlcnMKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRFVlRfMSwgdGl0bGUgPSAidmVudG8gbWFya2VyczogRVZUXzEiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMudmVudG8kRVZUXzIsIHRpdGxlID0gInZlbnRvIG1hcmtlcnM6IEVWVF8yIikKYGBgCgojIyMgU3VyeWEgbWFya2VycwpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJ2aWwuRVZUIl0sIHRpdGxlID0gInN1cnlhIG1hcmtlcnM6IHZpbC5FVlQiKQpgYGAKCiMjIFZDVCBhbmQgU0NUCmNsdXN0ZXJzIDE2LCAxNywgMjMsIDI0LCAyNSwgYW5kIDI5LgoKIyMjIE5vdGVzCkZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGV4cHJlc3Npb24gcGF0dGVybnMgYW5kIG1hcmtlcnMgb2YgZGlmZmVyZW50IHRyb3Bob2JsYXN0IHR5cGVzLCBzZWUgW0BsaXVfc2luZ2xlLWNlbGxfMjAxOF0uCgoxLiBJbiBhZGRpdGlvbiB0byBjbHVzdGVyIDEzIChFVlQpLCB0aGVyZSBhcmUgNiBvdGhlciBjbHVzdGVycyB0aGF0IGFyZSBsaWtlbHkgdHJvcGhvYmxhc3QgY2VsbCB0eXBlcyAodGhleSBleHByZXNzIEtSVDcgYW5kIGFyZSByZWxhdGVkIGNsdXN0ZXJzKS4gVGhlc2UgNiBjbHVzdGVycyBhcmUgb3JnYW5pemVkIGludG8gdHdvIGNvcnJlbGF0ZWQgYmxvY2tzIChzZWUgImNvcnJlbGF0aW9ucyBzZWN0aW9uIGFib3ZlIik6ICgxNiwgMTcsIDI1KSBhbmQgKDIzLCAyOSwgMjQpIG9mIHdoaWNoIHRoZSBmb3JtZXIgYXJlIGxpa2VseSBWQ1QgYW5kIHRoZSBsYXR0ZXIgU0NUIGFjY29yZGluZyB0byBjb3JyZWxhdGlvbi1iYXNlZCBhbm5vdGF0aW9uLiAKMi4gQ2x1c3RlciAyOSBhcHBlYXJzIHRvIGJlIFNDVCBnaXZlbiBpdHMgZXhwcmVzc2lvbiBvZiBDR0EsIEdERjE1LCBFUlZGUkQtMSAoU3luY3l0aW4gMikgZ2VuZXMuIEZvciBzb21lIHJlYXNvbiBHTyBlbnJpY2htZW50IGRpZG4ndCB3b3JrIGZvciBtYXJrZXJzIG9mIGNsdXN0ZXIgMjksIGJ1dCBpdCBkb2VzIGNvbnNpc3RlbnRseSBleHByZXNzIFNDVCBtYXJrZXJzIGZyb20gdmVudG8gYW5kIHN1cnlhLgozLiBDbHVzdGVyIDI0IGRvZXNuJ3QgZXhwcmVzcyBFUlZGUkQtMSBtdWNoLCBidXQgaXQgZG9lcyBleHByZXNzIG1vc3Qgb3RoZXIgdGVsbHRhbGUgZ2VuZXMgb2YgU0NUOiBDU0gxLCBDU0gyLCBDR0EsIENTSEwxLCBtYW55IFBTRyBnZW5lcywgR0gyLCBQR0YsIEdERjE1LCBldGMuIEFuZCBpdCBpcyBub3QgZW5yaWNoZWQgZm9yIGV4cHJlc3Npb24gb2YgVkNUIG1hcmtlcnMgZnJvbSB2ZW50byBhbmQgc3VyeWEuIEl0J3MgZW5yaWNoZWQgR08gdGVybXMgaGF2ZSB0byBkbyB3aXRoIGhvcm1vbmUgcHJvZHVjdGlvbiBhbmQgc2lnbmFsaW5nIGFzIGV4cGVjdGVkIGZvciBTQ1QuIFRodXMsIHRoaXMgaXMgbW9zdCBsaWtlbHkgYW4gU0NUIGNsdXN0ZXIuIAo0LiBDbHVzdGVycyAxNiBhbmQgMTcgZG9uJ3QgZXhwcmVzcyBtYW55IG1hcmtlcnMgb2YgU0NULCBidXQgZG8gZXhwcmVzcyBtYW55IHZlbnRvIGFuZCBzdXJ5YSBtYXJrZXJzIG9mIFZDVDogUEFHRTQsIFBFRzEwLCBYQUdFMywgZXRjLgo1LiBDbHVzdGVyIDI1IGhhcyBWQ1QgbWFya2VycyBhcyB3ZWxsIGFzIGEgc3Ryb25nIHNpZ25hdHVyZSBvZiBjZWxsIGRpdmlzaW9uIGdlbmVzLiBUaGlzIHNob3dzIHVwIGluIHRoZSBHTyBlbnJpY2htZW50IGFzIHdlbGwsIHdoZXJlIG1vc3QgZW5yaWNoZWQgR08gdGVybXMgYXJlICJudWNsZWFyIGRpdmlzaW9uIiwgImNocm9tb3NvbWUgc2VncmVnYXRpb24iLCAiY2VsbCBjeWNsZSBHMS9TIHBoYXNlIHRyYW5zaXRpb24iIGV0Yy4gVGhpcyBpcyBsaWtlbHkgYSBjbHVzdGVyIG9mIHByb2xpZmVyYXRpbmcgVkNUIGNlbGxzLiBJbmNpZGVudGFsbHkgYm90aCBbQHZlbnRvLXRvcm1vX3NpbmdsZS1jZWxsXzIwMThdIGFuZCBbQGxpdV9zaW5nbGUtY2VsbF8yMDE4XSBoYXZlIGEgc2VwYXJhdGUgcG9wdWxhdGlvbiBvZiBWQ1Qgd2l0aCBhIGNlbGwgY3ljbGUgZ2VuZSBzaWduYXR1cmUgKFZDVF8yIGluIHZlbnRvKS4gCjYuIENsdXN0ZXIgMjMgaXMgbW9zdCBzaW1pbGFyIHRvIFNDVCBvZiB2ZW50byBidXQgVkNUIG9mIHN1cnlhLiBJbmRlZWQgdGhpcyBjbHVzdGVyIGhhcyBleHByZXNzaW9uIG9mIG1hcmtlciBnZW5lcyBmb3IgYm90aCBWQ1QgYW5kIFNDVCAoc2VlIHBsb3RzKS4gSXQgZXhwcmVzc2VzIFBBR0U0LCBQRUcxMCBldGMsIGJ1dCBhbHNvIGV4cHJlc3NlcyBzeW5jeXRpbiBnZW5lIEVSVlctMS4gVGhpcyBjb3VsZCByZXByZXNlbnQgYSBncm91cCBvZiBjeXRvdHJvcGhvYmxhc3QgY2VsbHMgdGhhdCBhcmUgaW4gdGhlIHByb2Nlc3Mgb2YgZGlmZmVyZW50aWF0aW5nIHRvIHN5Y255dGlhbCB0cm9waG9ibGFzdC4gV2Ugd2lsbCBsYWJlbCBpdCBhcyBhIFZDVCB0eXBlLiAKCmBgYHtyfQphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMTYiKV0gPC0gInZpbC5WQ1RfMSIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzE3IildIDwtICJ2aWwuVkNUXzIiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8yMyIpXSA8LSAidmlsLlZDVF8zIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMjUiKV0gPC0gInZpbC5WQ1RfNCIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzI0IildIDwtICJ2aWwuU0NUXzEiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8yOSIpXSA8LSAidmlsLlNDVF8yIgoKYW5ub3RhdGlvbiRub3Rlc1thbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8yNSIpXSA8LSAicHJvbGlmZXJhdGluZyIKCmFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgcGFzdGUwKCJjbHVzdF8iLCBjKCIxNiIsICIxNyIsICIyMyIsICIyNSIsICIyNCIsICIyOSIpKSwgXQpgYGAKCiMjIyBNYXJrZXIgZ2VuZSBwbG90cwpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMTYiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMTciXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMjUiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMjMiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMjQiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMjkiXV0KYGBgCgojIyMgR08gZW5yaWNobWVudApgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzE2IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzE3IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzI1IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzIzIikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzI0IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzI5IikKYGBgCgojIyMgVmVudG8gbWFya2VycwpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJFZDVF8xLCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBWQ1RfMSIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRWQ1RfMiwgdGl0bGUgPSAidmVudG8gbWFya2VyczogVkNUXzIiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMudmVudG8kU0NULCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBTQ1QiKQpgYGAKCiMjIyBTdXJ5YSBtYXJrZXJzCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMuc3VyeWEkZ2VuZVttYXJrZXJzLnN1cnlhJGNsdXN0ZXIgPT0gInZpbC5WQ1QiXSwgdGl0bGUgPSAic3VyeWEgbWFya2VyczogdmlsLlZDVCIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy5zdXJ5YSRnZW5lW21hcmtlcnMuc3VyeWEkY2x1c3RlciA9PSAidmlsLlNDVCJdLCB0aXRsZSA9ICJzdXJ5YSBtYXJrZXJzOiB2aWwuU0NUIikKYGBgCgojIyMgUGF2bGkgbWFya2VycwpgYGB7ciBmaWcuYXNwPTAuNCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBjKCJBRFJCMSIsICJQVUY2MCIsICJTTk9SREEzQSIsICJQUk1UNyIsICJFMUYxQVkiLCAKICAgICAgICAgICAgICAgICAgICAgICJGWFlEMyIsICJHUkFNRDIiLCAiSU5TTDQiLCAiSVRHQjgiLCAiUEFHRTQiLAogICAgICAgICAgICAgICAgICAgICAgIlNMQzEzQTQiLCAiU0xDMjJBMTEiKSwgCiAgICAgICAgIHRpdGxlID0gInBhdmxpIG1hcmtlcnM6IFZDVF8xIikKYGBgCgoKYGBge3IgZmlnLmFzcD0wLjYsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gYygiWEFHRTMiLCAiWEFHRTIiLCAiU0VSSU5DNSIsICJTMTAwUCIsICJSQVNBMSIsCiAgICAgICAgICAgICAgICAgICAgICAiTUZBUDUiLCAiTElOMjhCIiwgIktSVDgiLCAiS1JUNyIsICJJTlMtSUdGMiIsICJHQ00xIiwKICAgICAgICAgICAgICAgICAgICAgICJHQVRBMyIsICJFUlZXLTEiLCAiRVJWRlJELTEiLCAiRUdSMSIsICJFRk5BMSIsIAogICAgICAgICAgICAgICAgICAgICAgIkNZUDE5QTEiLCAiUEhMREEyIiwgIkFDS1IyIiksIAogICAgICAgICB0aXRsZSA9ICJwYXZsaSBtYXJrZXJzOiBWQ1RfMiIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gYygiU0VNQTNCIiwgIkNTSDIiLCAiQ1NITDEiLCAiRkNHUjJBIiwgIkdIMiIsICJIQkExIiwgCiAgICAgICAgICAgICAgICAgICAgICAiSEJBMiIsICJIQkIiLCAiSEJHMSIsICJIQkcyIiwgIkhMQS1ETUEiLCAiSExBLURQQTEiLAogICAgICAgICAgICAgICAgICAgICAgIkhMQS1EUUIxIiwgIkhMQS1EUkEiLCAiSExBLURSQjEiLCAiSFBHRFMiLCAiQ0dCOCIsCiAgICAgICAgICAgICAgICAgICAgICAiTEdBTFMxNCIsICJMWVZFMSIsICJOUElQQjMiLCAiQ0dCNSIsICJQU0cxIiwgIlBTRzMiLCAKICAgICAgICAgICAgICAgICAgICAgICJQU0c2IiwgIlBTRzkiLCAiWk5GMTE3IiwgIlpORjkxIiwgIkhJU1QySDJBQiIsCiAgICAgICAgICAgICAgICAgICAgICAiUkFNUDIiLCAiTVVDMjAiKSwgCiAgICAgICAgIHRpdGxlID0gInBhdmxpIG1hcmtlcnM6IFNDVCIpCmBgYAoKIyMjIExpdSBldCBhbCBtYXJrZXJzIGZvciB0cm9waG9ibGFzdHMKYGBge3IgZmlnLmFzcD0wLjV9CkRvdFBsb3QyKHNldXIsIGZlYXR1cmVzID0gYygiR0NNMSIsICJDU0gxIiwgIktSVDciLCAiVEZBUDJDIiwgIkdBVEEzIiwgIkNESDEiLCAiRUdGUiIsICJITEEtRyIsICJNTVAyIiwgIlJSTTIiLCAiQ0NOQjEiLCAiQ0RLMSIsICJFUlZGUkQtMSIsICJTTEMxQTUiLCAiUEFHRTQiLCAiQ0dCIiksIHRpdGxlID0gIkdlbmVzIGZyb20gTGl1IGV0IGFsIDIwMTgiKQpgYGAKIyMgRmlicm9ibGFzdHMgYW5kIFNNQwpDbHVzdGVycyAwMiwgMDYsIDA3LCAxNSwgMTksIGFuZCAzMy4KCiMjIyBOb3RlcwoKMS4gTW9zdCBvZiB0aGVzZSBjbHVzdGVycyByZXByZXNlbnQgc29tZSBraW5kIG9mIGZpYnJvYmxhc3Qgb3IgYSBjbG9zZWx5IHJlbGF0ZWQgY2VsbCB0eXBlLiBNb3N0IGNsdXN0ZXJzIGFyZSBlbnJpY2hlZCBmb3IgR08gdGVybXMgcmVsYXRlZCB0byBFQ00gcmVndWxhdGlvbi4gCjIuIENsdXN0ZXJzIDAyIGFuZCAxNSBtYXJrZXIgZ2VuZXMgYXJlIGFsc28gaGlnaGx5IGV4cHJlc3NlZCBpbiBjbHVzdGVycyAwMCBhbmQgMTguIENsdXN0ZXIgMTUgaGFzIG1vcmUgdGhhbiA3NSUgY2VsbHMgY29taW5nIGZyb20gZGVjaWR1YSBzYW1wbGVzLCBhbmQgY2x1c3RlciAwMiBoYXMgb3ZlciA1MCUgY2VsbHMgY29taW5nIGZyb20gZGVjaWR1YSBzYW1wbGVzLiBEZWNpZHVhbCBzdHJvbWFsIGNlbGxzIGFyZSBrbm93biB0byBiZSBhY3R1YWxseSBhIGZldyBkaWZmZXJlbnQgcG9wdWxhdGlvbnMuIEV2ZW4gaW4gVmVudG8tdG9ybW8gZXQgYWwsIHRoZXkgYXJlIGxhYmVsZWQgYXMgZFMxLS0zLCBvZiB3aGljaCBkUzMgYXJlIHRoZSBjYW5vbmljYWwgRFNDIHdpdGggUFJMIGFuZCBJR0ZCUDEsIHdoaWxlIGRTMSBhbmQgZFMyIGFyZSBjbG9zZWx5IHJlbGF0ZWQgdG8gRFNDIGFuZCBhcmUgZGVyaXZlZCBmcm9tIGVuZG9tZXRyaWFsIHN0cm9tYWwgZmlicm9ibGFzdHMuIFRoZXNlIHRoaW5ncyB0b2dldGhlciBzdWdnZXN0IHRoYXQgY2x1c3RlcnMgMDIgYW5kIDE1IGFyZSB0aGUgbm9uLVBSTCB2YXJpZXRpZXMgb2YgRFNDLiBUbyBkaXN0aW5ndWlzaCB0aGVtIGZyb20gRFNDLCB3ZSBjYW4gY2FsbCB0aGVtIGRlY2lkdWFsIGZpYnJvYmxhc3RzLiAKMy4gTWFya2VyIGdlbmVzIG9mIDA2LCAwNywgYW5kIDMzIHNob3cgaGlnaGx5IGNvcnJlbGF0ZWQgZ2VuZSBleHByZXNzaW4sIGkuZS4gdGhlIG1hcmtlcnMgb24gZWFjaCBvZiB0aGVzZSBjbHVzdGVycyBhcmUgYWxzbyBoaWdobHkgZXhwcmVzc2VkIGluIHRoZSBvdGhlciB0d28gY2x1c3RlcnMsIHN1Z2dlc3RpbmcgdGhhdCB0aGVzZSB0aHJlZSBjbHVzdGVycyBhIGNsb3NlbHkgcmVsYXRlZCBjZWxsIHBvcHVsYXRpb25zLiBBbGwgdGhyZWUgb2YgdGhlc2UgY2x1c3RlcnMgYXJlIGxhcmdlbHkgKG92ZXIgNzUlIG9mIHRoZSBjZWxscykgZGVyaXZlZCBmcm9tIHZpbGxpIHNhbXBsZXMsIGFuZCB0aGV5IGFyZSBmaWJyb2JsYXN0LWxpa2UgY2VsbHMuIFRodXMsIHRoZXNlIGFyZSBsaWtlbHkgdmlsbG91cyBmaWJyb2JsYXN0cy4KNC4gQ2x1c3RlciAxOSBpcyBtb3N0IHNpbWlsYXIgdG8gUFYgKHBlcml2YXNjdWxhcikgY2x1c3RlcnMgZnJvbSB2ZW50byBhbmQgU01DIChzbW9vdGggbXVzY2xlIGNlbGwpIGNsdXN0ZXIgZnJvbSBzdXJ5YS4gSW5kZWVkLCBpdHMgbWFya2VycyBpbmNsdWRlIHNtb290aCBtdXNjbGUgZ2VuZXMgbGlrZSBNR1AsIE1ZSDExLCBNWUw5IGV0YywgYW5kIGVucmljaGVkIEdPIHRlcm1zIGZvciB0aGlzIGNsdXN0ZXIgaW5jbHVkZSAibXVzY2xlIGNvbnRyYWN0aW9uIiBhbmQgImFydGVyeSBtb3JwaG9nZW5lc2lzIi4gVGh1cywgdGhpcyBjbHVzdGVyIGlzIG1vc3QgbGlrZWx5IG9mIHBlcml2YXNjdWxhciBzbW9vdGggbXVzY2xlIGNlbGxzIG9mIGRlY2lkdWFsIG9yaWdpbiAob3ZlciA3NSUgY2VsbHMgZnJvbSBkZWNpZHVhIHNhbXBsZXMpLiAKCmBgYHtyfQphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMDIiKV0gPC0gImRlYy5GQl8xIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMTUiKV0gPC0gImRlYy5GQl8yIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMDYiKV0gPC0gInZpbC5GQl8xIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMDciKV0gPC0gInZpbC5GQl8yIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMzMiKV0gPC0gInZpbC5GQl8zIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMTkiKV0gPC0gImRlYy5TTUMiCgphbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIHBhc3RlMCgiY2x1c3RfIiwgYygiMDIiLCAiMTUiLCAiMDYiLCAiMDciLCAiMzMiLCAiMTkiKSksIF0KYGBgCgojIyMgTWFya2VyIGdlbmUgcGxvdHMKYGBge3IgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90c1tbImNsdXN0XzAyIl1dCmBgYAoKYGBge3IgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90c1tbImNsdXN0XzE1Il1dCmBgYAoKYGBge3IgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90c1tbImNsdXN0XzA2Il1dCmBgYAoKYGBge3IgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90c1tbImNsdXN0XzA3Il1dCmBgYAoKYGBge3IgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90c1tbImNsdXN0XzE5Il1dCmBgYAoKYGBge3IgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90c1tbImNsdXN0XzMzIl1dCmBgYAoKIyMjIEdPIGVucmljaG1lbnQKYGBge3IgbWVzc2FnZT1GQUxTRX0KZW5yaWNoR08yKCJjbHVzdF8wMiIpCmBgYAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KZW5yaWNoR08yKCJjbHVzdF8xNSIpCmBgYAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KZW5yaWNoR08yKCJjbHVzdF8wNiIpCmBgYAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KZW5yaWNoR08yKCJjbHVzdF8wNyIpCmBgYAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KZW5yaWNoR08yKCJjbHVzdF8xOSIpCmBgYAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KZW5yaWNoR08yKCJjbHVzdF8zMyIpCmBgYAoKIyMjIFZlbnRvIG1hcmtlcnMKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMudmVudG8kUFYxLCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBQVjEiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMudmVudG8kUFYyLCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBQVjIiKQpgYGAKCiMjIyBTdXJ5YSBtYXJrZXJzCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMuc3VyeWEkZ2VuZVttYXJrZXJzLnN1cnlhJGNsdXN0ZXIgPT0gInZpbC5GQjEiXSwgdGl0bGUgPSAic3VyeWEgbWFya2VyczogdmlsLkZCMSIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy5zdXJ5YSRnZW5lW21hcmtlcnMuc3VyeWEkY2x1c3RlciA9PSAiZGVjLlNNQyJdLCB0aXRsZSA9ICJzdXJ5YSBtYXJrZXJzOiBkZWMuU01DIikKYGBgCgojIyBCIGNlbGxzCkNsdXN0ZXIgMjAuCgojIyMgTm90ZXMKVGhpcyBpcyBtb3N0IGxpa2VseSBhIGNsdXN0ZXIgb2YgQiBjZWxscy4gTW9zdCBtYXJrZXJzIGdlbmVzIGFyZSByZWxhdGVkIHRvIEIgY2VsbHM6IG1hbnkgaW1tdW5vZ2xvYnVsaW4gZ25lZXMsIFNQSUIsIE1TNEExLCBDRDc5QSwgQ0Q3OUIsIEJBTksxLCBldGMuIEl0IGFsc28gZXhwcmVzc2VzIG1hbnkgZ2VuZXMgdGhhdCBhcmUgbWFya2VycyBmb3IgUGxhc21hIGNsdXN0ZXIgZnJvbSB2ZW50byBkYXRhc2V0LiBHTyBlbnJpY2htZW50IGFsc28gYWdyZWVzLgoKYGBge3J9CmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8yMCIpXSA8LSAiQmNlbGwiCgphbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIHBhc3RlMCgiY2x1c3RfIiwgYygiMjAiKSksIF0KYGBgCgojIyMgTWFya2VyIGdlbmVzIHBsb3QKYGBge3IgZmlnLmFzcD0xfQp0b3A1MG1hcmtlci5wbG90c1tbImNsdXN0XzIwIl1dCmBgYAoKIyMjIEdPIGVucmljaG1lbnQKYGBge3IgbWVzc2FnZT1GQUxTRX0KZW5yaWNoR08yKCJjbHVzdF8yMCIpCmBgYAoKIyMjIFZlbnRvIG1hcmtlcnMKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRQbGFzbWEsIHRpdGxlID0gInZlbnRvIG1hcmtlcnM6IFBsYXNtYSIpCmBgYAoKIyMgT3RoZXIgbHltcGhvY3l0ZXMKQ2x1c3RlcnMgIDA1LCAwOSwgMTAsIDE0LCAzMCwgYW5kIDMyLgoKIyMjIE5vdGVzCkZvciBjbHVlcyBvbiBtYXJrZXIgZ2VuZXMgZm9yIHZhcmlvdXMgdHlwZXMgb2YgbHltcGhvY3l0ZXMgc2VlIFtAcGl6em9sYXRvX3NpbmdsZS1jZWxsXzIwMTldLgoKMS4gVGhlc2UgYXJlIGxpa2VseSBub24tQiBseW1waG9pZCBjZWxscy4gCjIuIENsdXN0ZXJzIDA5IGFuZCAzMiBhcmUgbW9zdCBzaW1pbGFyIHRvIFRjZWxscyBmcm9tIGJvdGggdmVudG8gYW5kIHN1cnlhLiBHaXZlbiB0aGUgZXhwcmVzc2lvbiBvZiBDRDMgZ2VuZXMsIHRoYXQgYXBwZWFycyB0byBiZSB0aGUgY29ycmVjdCBhc3NpZ25tZW50LiAKICAgICArIFRoZSBjbHVzdGVycyBhbHNvIGV4cHJlc3MgVFJBQyBhbmQgVFJCQzEgKGNvbnN0YW50IHJlZ2lvbnMgb2YgVENSIGFscGhhIGFuZCBiZXRhKSwgbWFraW5nIHRoZW0gYWxwaGEtYmV0YSBUIGNlbGxzLiAKICAgICArIEl0J3MgaGFyZCB0byB0ZWxsIGlmIHRoZXkgYXJlIENENCBvciBDRDgtLS1ib3RoIENENCBhbmQgQ0Q4IGdlbmVzIGFyZSBleHByZXNzZWQgYnkgYSBzbWFsbCBmcmFjdGlvbiBvZiBjZWxscyBpbiBib3RoIGNsdXN0ZXJzLCBidXQgQ0Q4IGdlbmVzIGV4cHJlc3NlZCBhdCBhIGhpZ2hlciBsZXZlbC4gCiAgICAgKyBHTyBlbnJpY2htZW50IGZvciBib3RoIGluY2x1ZGUgdGVybXMgcmVsYXRlZCB0byBUIGNlbGwgZGlmZmVyZW50aWF0aW9uLCBUIGNlbGwgYWN0aXZhdGlvbiBldGMuIAogICAgICsgVGhlc2UgY2x1c3RlcnMgY29udGFpbiBjZWxscyBmcm9tIGJvdGggZGVjaWR1YSBhbmQgdmlsbGkgc2FtcGxlcy4gCjMuIENsdXN0ZXIgMDUgaXMgbW9zdCBzaW1pbGFyIHRvIE5LIGNlbGxzIGZyb20gdmVudG8gYW5kIHN1cnlhLiBCdXQgYmFzZWQgb24gdGhlIGxhY2sgb2YgTktHNyBhbmQgTkNBTTEgZXhwcmVzc2lvbiBhbmQgZXhwcmVzc2lvbiBvZiBDRDMgZ2VuZXMsIFRSQUMsIGFuZCBUUkJDMSB0aGlzIGlzIGxpa2VseSBhIFQgY2VsbCBjbHVzdGVyLiBPbmUgb2YgaXRzIG1hcmtlciBnZW5lcyBiZWluZyBHWk1BIGFuZCBpdHMgb3ZlcmFsbCBzaW1pbGFyaXR5IHdpdGggTksgY2VsbHMgc3VnZ2VzdCB0aGF0IHRoaXMgbWF5IGJlIGEgY3l0b3RveGljIFQgY2VsbC4gQ29uc2lzdGVudGx5LCBhbW9uZyBpdHMgZW5yaWNoZWQgR08gdGVybXMgYXJlICJUIGNlbGwgbWVkaWF0ZWQgY3l0b3RveGljaXR5IiBhbmQgIkNlbGwga2lsbGluZyIuIFRoaXMgY2x1c3RlciBtb3N0bHkgY29udGFpbnMgY2VsbHMgZnJvbSBkZWNpZHVhIHNhbXBsZXMuCjQuIENsdXN0ZXJzIDEwLCAxNCwgYW5kIDMwIGFyZSBOSyBjZWxscyBiYXNlZCBvbiB0aGUgZXhwcmVzc2lvbiBvZiBOS0c3IGFuZCBOQ0FNMS4gCiAgICAgKyBUaGVzZSBjbHVzdGVycyBhcmUgYWxzbyBleHByZXNzIEdaTUEsIEdaTUIsIEdOTFksIFBSRjEsIEtMUkIxLCBLTFJDMSwgS0xSRDEuIAogICAgICsgQ2x1c3RlciAzMCBpcyBlbnJpY2hlZCBmb3IgY2VsbCBjeWNsZSBnZW5lcywgc28gcmVwcmVzZW50cyBwcm9saWZlcmF0aW5nIGNlbGxzLiBWZW50byBhbmQgc3VyeWEgZGF0YXNldHMgYWxzbyBoYXZlIGEgY2x1c3RlciBvZiBwcm9saWZlcmF0aW5nIE5LIGNlbGxzIChkTksucCBpbiB2ZW50byBhbmQgTksyIGluIHN1cnlhKS4gKwogICAgICsgQ2x1c3RlciAzMCBpbiB1bmFtYmlndW91c2x5IG9mIGRlY2lkdWFsIHNhbXBsZSBvcmlnaW4uIENsdXN0ZXJzIDEwIGFuZCAxNCBhcmUgbWl4ZWQuIAogICAgIApgYGB7cn0KYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzA1IildIDwtICJUY2VsbF8xIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMDkiKV0gPC0gIlRjZWxsXzIiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8xMCIpXSA8LSAiTktfMSIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzE0IildIDwtICJOS18yIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMzAiKV0gPC0gIk5LXzMiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8zMiIpXSA8LSAiVGNlbGxfMyIKCmFubm90YXRpb24kbm90ZXNbYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMDUiKV0gPC0gImN5dG90b3hpYyIKYW5ub3RhdGlvbiRub3Rlc1thbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8zMCIpXSA8LSAicHJvbGlmZXJhdGluZyIKCgphbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIHBhc3RlMCgiY2x1c3RfIiwgYygiMDUiLCAiMDkiLCAiMTAiLCAiMTQiLCAiMzAiLCAiMzIiKSksIF0KYGBgCgojIyMgTHltcGhvY3l0ZSBjYW5kaWRhdGUgZ2VuZXMKYGBge3IgZmlnLmFzcD0wLjV9CkRvdFBsb3QyKGZlYXR1cmVzID0gYygiTkNBTTEiLCAiTktHNyIsICJDRDNEIiwgIkNEM0ciLCAiQ0QzRSIsICJUUkFDIiwgIlRSQkMxIiwgIlRSR0MyIiwgIlRSREMiLCAiQ0Q4QSIsICJDRDhCIiwgIkNENCIpLCB0aXRsZSA9ICJMeW1waG9jeXRlIGNhbmRpZGF0ZSBnZW5lcyIpCmBgYAoKIyMjIE1hcmtlciBnZW5lIHBsb3RzCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8wNSJdXQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8wOSJdXQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8xMCJdXQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8xNCJdXQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8zMCJdXQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KdG9wNTBtYXJrZXIucGxvdHNbWyJjbHVzdF8zMiJdXQpgYGAKCiMjIyBHTyBlbnJpY2htZW50CmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMDUiKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMDkiKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMTAiKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMTQiKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMzAiKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmVucmljaEdPMigiY2x1c3RfMzIiKQpgYGAKCiMjIyBWZW50byBtYXJrZXJzCgpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJFRjZWxsc18yLCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBUY2VsbHNfMiIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRUY2VsbHNfNCwgdGl0bGUgPSAidmVudG8gbWFya2VyczogVGNlbGxzXzQiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMudmVudG8kZE5LMSwgdGl0bGUgPSAidmVudG8gbWFya2VyczogZE5LMSIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRkTktwLCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBkTktwIikKYGBgCgoKIyMjIFN1cnlhIG1hcmtlcnMKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy5zdXJ5YSRnZW5lW21hcmtlcnMuc3VyeWEkY2x1c3RlciA9PSAiZGVjLk5LMSJdLCB0aXRsZSA9ICJzdXJ5YSBtYXJrZXJzOiBkZWMuTksxIikKYGBgCgpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJkZWMuTksyIl0sIHRpdGxlID0gInN1cnlhIG1hcmtlcnM6IGRlYy5OSzIiKQpgYGAKCiMjIE15ZWxvaWQgY2VsbHMKQ2x1c3RlcnMgMDEsIDAzLCAwOCwgMTEsIDEyLCAyMiwgMjYsIDI3LCAyOCwgMzEsIGFuZCAzNC4KCiMjIyBOb3RlcwoKMS4gQ2x1c3RlcnMgMDEsIDIyLCAzMSwgMzQsIGFuZCAyNyBmb3JtIGEgZ3JvdXAgaW4gdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGFib3ZlLCBhbmQgdGhleSBhbGwgaGF2ZSBtb3N0IGNlbGxzIChtb3JlIHRoYW4gNzUlKSBjb21pbmcgZnJvbSB2aWxsaSBzYW1wbGVzLiAKICAgICsgQWxsIGZpdmUgY2x1c3RlcnMgYXJlIGhpZ2hseSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIGVhY2ggb3RoZXIsIGVzcGVjaWFsbHkgY2x1c3RlcnMgMDEsIDMxLCBhbmQgMzQuIAogICAgKyBBbGwgY2x1c3RlcnMgZXhwcmVzcyB2YXJpb3VzIG1hY3JvcGhhZ2UgZ2VuZXM6IENENjgsIENEMTYzLCBNUkMxLCBNQUYsIEMxUUMsIEMxUUEsIEZPTFIyIGV0YzsgYW5kIG1vcmUgc28gY2x1c3RlcnMgMDEsIDMxLCBhbmQgMzQuIAogICAgKyBDbHVzdGVycyAwMSBhbmQgMzEgYXJlIG1vc3QgbGlrZWx5IHZpbGxvdXMgbWFjcm9waGFnZXMgKGkuZS4gSG9mYmF1ZXIgY2VsbHMpLCBidXQgdGhlIG90aGVyIGNsdXN0ZXIgbmVlZCBtb3JlIGludmVzdGlnYXRpb24uIAogICAgKyBDbHVzdGVycyAyMiBhbmQgMjcsIGV2ZW4gdGhvdWdoIGNvcnJlbGF0ZWQgd2l0aCAwMSBhbmQgMzQsIGhhdmUgbWFya2VyIGdlbmVzIHRoYXQgYXJlIG5vdCB0aGF0IGhpZ2hseSBleHByZXNzZWQgaW4gdGhlbXNlbHZlcyBidXQgYXJlIGluIDAxIGFuZCAzNC4gTG9va2luZyBhdCB0aGUgVU1BUCBwbG90IGJlbG93LCBpdCdzIGNsZWFyIHRoYXQgYm90aCBvZiB0aGVzZSBjbHVzdGVycyBhcmUgbm90IGFzIGhvbW9nZW5vdXMgYXMgdGhlIG90aGVyLiBUaGUgY2VsbHMgYXJlIHNwcmVhZCBiZXR3ZWVuIHRoZSBiaWcgbXllbG9pZCBibG9iIGFuZCB0aGUgYmlnIGZpYnJvYmxhc3QgYmxvYi4gCiAgICArIEhvd2V2ZXIsIHRoZXNlIGRvbid0IGFwcGVhciB0byBiZSBjbHVzdGVycyBvZiBkaWZmZXJlbnQgdHlwZXMgb2YgY2VsbHMuIFRoYXQgaXMsIGZpYnJvYmxhc3QgZ2VuZXMgKENPTDNBMSwgQ09MM0EzKSBhbmQgbWFjcm9waGFnZSBnZW5lcyBhcmUgY29leHByZXNzZWQgaW4gdGhlIHNhbWUgY2VsbHMgaW4gY2x1c3RlciAyNyAoc2VlIHBsb3QgYmVsb3cpLiBBbmQgbWFjcm9waGFnZSBnZW5lcyBhcmUgYnJvYWRseSBleHByZXNzZWQgaW4gY2x1c3RlciAyMiwgZXZlbiBpbiBjZWxscyB0aGF0IHNlZW0gdG8gYmUgZW1iZWRkZWQgd2l0aCBmaWJyb2JsYXN0IGNlbGxzLiBDbHVzdGVycyAyMiBhbmQgMjcgbWlnaHQgcmVwcmVzZW50IGNlbGwgdHlwZXMgdGhhdCBhcmUgbWFjcm9waGFnZXMgYnV0IHdpdGggY2VydGFpbiBmaWJyb2JsYXN0LWxpa2UgcHJvcGVydGllcy4gCiAgICArIExvb2tpbmcgYXQgY2x1c3RlciAzNCBtYXJrZXJzIGdlbmVzLCBpdCBpbnRlcmVzdGluZ2x5IGV4cHJlc3NlcyBtYW55IGVyeXRocm9jeXRlIGxpbmVhZ2UgZ2VuZXMgKEhCQTEsIEhCQTIsIEhCTSwgQ0QxLCBIQkcxLCBDRDIzNWEvR1lQQSkgYnV0IGl0IGFsc28gZXhwcmVzc2VzIG1hY3JvcGhhZ2UgZ2VuZXMgbGlrZSBDU0YxUiwgRk9MUjIsIE1SQzEsIENEMTYzIGV0Yy4gSWYgd2UgcGxvdCB0aGUgZXhwcmVzc2lvbiBvZiB0aGVzZSBnZW5lcyBvbiBVTUFQIChwbG90dGluZyBvbmx5IGNsdXN0XzM0LCBzZWUgYmVsb3cpLCB3ZSBzZWUgdGhhdCBtYWpvcml0eSBvZiB0aGUgY2VsbHMgZXhwcmVzcyBtYWNyb3BoYWdlIGdlbmVzLCB3aGlsZSB0aGUgZXJ5dGhyb2N5dGUgZ2VuZXMgYXJlIGV4cHJlc3NlZCBpbiBhIHNtYWxsIG51bWJlciBvZiBjZWxscyAoZXhjZXB0IEhCRzIpLCBzdWdnZXN0aW5nIHRoYXQgdGhpcyBjbHVzdGVyIGlzIG1hZGUgb2YgdHdvIGNlbGwgdHlwZXMuIFNpbmNlIG1ham9yaXR5IG9mIHRoZSBjZWxscyBpbiB0aGlzIGNsdXN0ZXIgYXJlIG1hY3JvcGhhZ2UgY2VsbHMsIHdlIHdpbGwgbGFiZWwgaXQgYXMgc3VjaCwgYnV0IHNob3VsZCB0aGUgbmVlZCBhcmlzZSBpbiBmdXJ0aGVyIGFuYWx5c2lzLCB3ZSBjYW4gc3ViY2x1c3RlciBpdCBmdXJ0aGVyIHRvIHNlcGFyYXRlIHRoZSB0d28gY2VsbCB0eXBlcyBvdXQuCjIuIENsdXN0ZXIgMjYgaXMgbW9zdCBsaWtlbHkgb2YgZXJ5dGhyb2N5dGVzIChvciBzb21lIGNlbGwgdHlwZSBmcm9tIHRoZSBlcnl0aHJvY3l0ZSBsaW5lYWdlKS4gCiAgICArIFRoZSBzdXJ5YSBkYXRhc2V0IGRvZXMgY29udGFpbiB3aGF0IHRoZXkgY2FsbCBhbiBlcnl0aHJvYmxhc3QgY2x1c3RlciwgYnV0IGluIGVyeXRocm9wb2Vpc2lzIGlzIGV4cGVjdGVkIGluIHRoZSBwbGFjZW50YSBpbiB0aGUgZmlyc3QgdHJpbWVzdGVyLiBJbiBvdXIgdGVybSBzYW1wbGVzLCBJIGFtIG5vdCBzdXJlIGlmIGVyeXRocm9jeXRlIHByb2dlbml0b3JzIGFyZSBleHBlY3RlZCB0byBiZSBwcmVzZW50LiAKICAgICsgTWFya2VyIGdlbmVzLCBzaW1pbGFyaXR5IHdpdGggc3VyeWEgRUIgY2x1c3RlciwgYW5kIEdPIGVucmljaGVtZW50ICh3aGljaCBpbmNsdWRlICJlcnl0aHJvY3l0ZSBkZXZlbG9wbWVudCIsICJveHlnZW4gdHJhbnNwb3J0IiwgZXRjKSBhcmUgYWxsIGNvbnNpc3RlbnQgd2l0aCBlYWNoIG90aGVyLiAKICAgICsgTW9zdCBvZiB0aGUgY2VsbHMgaW4gdGhpcyBjbHVzdGVyIGNvbWUgZnJvbSB2aWxsaSBzYW1wbGVzLCBzbyBmZXRhbCBvcmlnaW4uCjMuIENsdXN0ZXIgMjggbW9zdCBsaWtlbHkgaXMgbWFkZSBvZiB0eXBpY2FsIGdyYW51bG9jeXRlcy4gCiAgICArIEl0IGV4cHJlc3NlcyB0aGUgZ3JhbnVsb2N5dGUgbWFya2VyIGdlbmUgQ0VBQ0FNOCwgaW4gYWRkaXRpb24gdG8gQ0VBQ0FNNiwgRUxBTkUsIGFuZCBNUE8uIAogICAgKyBHTyBlbnJpY2htZW50IGlzIGNvbnNpc3RlbnQuCiAgICArIEFsbCBtYXJrZXIgZ2VuZXMgYXJlIGNvbnNpc3RlbnQ6IExZWiwgREVGQTMsIERFRkE0LCBTMTAwQTgsIE5DRjEsIGV0Yy4gCjQuIENsdXN0ZXIgMTEgYW5kIDEyIGFyZSBtb3N0IGxpa2VseSBBUEMuCiAgICArIE9mIG1hdGVybmFsIG9yaWdpbi4gQ2x1c3RlciAxMiBtb3N0bHkgY29tZXMgZnJvbSBkZWNpZHVhIHNhbXBsZSwgY2x1c3RlciAxMSBoYXMgbWl4ZWQgY29udHJpYnV0aW9uLCBpbiB0ZXJtcyBvZiBpdHMgZ2VuZSBleHByZXNzaW9uIGl0J3MgdmVyeSBzaW1pbGFyIHRvIGNsdXN0ZXIgMTIuIAogICAgKyBWZXJ5IHNpbWlsYXIgdG8gZWFjaCBvdGhlci4gQW1vbmcgdmVudG8gY2x1c3RlciwgbW9zdCBzaW1pbGFyaXR5IHRvIG1hY3JvcGhhZ2UgY2x1c3RlcnMuIEFtb25nIHN1cnlhIGNsdXN0ZXJzLCBtb3N0IHNpbWlsYXJpdHkgdG8gbWF0ZXJuYWwgREMgYW5kIG1hdGVybmFsIEFQQyBjbHVzdGVycy4KICAgICsgRXhwcmVzc2lvbiBvZiBjbGFzc2ljYWwgbWFjcm9waGFnZS9EQyBtYXJrZXJzIGxpa2UgQ0Q2OCwgQ0QxNjMuIEluIGFkZGl0aW9uIG1vc3Qgb2YgdGhlIG1hcmtlciBnZW5lcyBvZiB0aGlzIGNsdXN0ZXIgaGF2ZSB0byBkbyB3aXRoIGFudGlnZW4gcHJlc2VudGF0aW9uLiBNYW55IG9mIHRoZW0gYXJlIE1IQy1JSSBnZW5lcy4gCiAgICArIE5vdCBzdXJlIGlmIHRoZXkgYXJlIG1hY3JvcGhhZ2VzIG9yIERDcywgYnV0IHRvIGF2b2lkIGNvbW1pdHRpbmcgdG8gb25lIGNvbmNsdXNpb24gb3ZlciB0aGUgb3RoZXIsIHdlIGNhbiBqdXN0IGxhYmVsIHRoZW0gYXMgQVBDcy4gCjUuIENsdXN0ZXJzIDMgYW5kIDggYXJlIGRpZmZpY3VsdC4KICAgICsgTG93LCBpZiBhbnksIGV4cHJlc3Npb24gb2YgQ0Q2OCBhbmQgQ0QxNjMsIHRodXMgbm90IHF1aXRlIG1hY3JvcGhhZ2VzLiBCdXQgYXQgbGVhc3QgY2x1c3RlciA4IGlzIHF1aXRlIHNpbWlsYXIgdG8gbWFjcm9waGFnZXMgaW4gb3ZlcmFsbCBnZW5lIGV4cHJlc3Npb24gKHNlZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBhYm92ZSkuCiAgICArIExvdywgaWYgYW55LCBleHByZXNzaW9uIG9mIEhMQSBnZW5lcywgdGh1cyBub3QgcXVpdGUgQVBDcy4KICAgICsgTm8gZXhwcmVzc2lvbiBvZiB0eXBpY2FsIGdyYW51bG9jeXRlIG1hcmtlcnM6IENFQUNBTTgsIENFQUNBTTYsIE1QTywgRUxBTkUuIEJ1dCBtYW55IG1hcmtlcnMgb2YgdGhlc2UgY2x1c3RlcnMgc291bmQgImdyYW51bG9jeXRpYyIuIFRoaXMgaXMgZXZpZGVudCBpbiBHTyBlbnJpY2htZW50IGFzIHdlbGwuIEluIGFkZGl0aW9uLCBjbHVzdGVyIDMgaXMgbW9zdCBzaW1pbGFyIHRvIGNsdXN0ZXIgMjggKHNlZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBhYm92ZSkgd2hpY2ggaXMgY2xlYXJseSBhIGdyYW51bG9jeXRlIGNsdXN0ZXIuCiAgICArIENsdXN0ZXIgMDggaXMgbW9zdCBzaW1pbGFyIHRvIHRoZSBNT18xIGFuZCBNT18yIGNsdXN0ZXJzIGZyb20gdmVudG8uIENsdXN0ZXIgMyBoYWQgbG93IHNpbWlsYXJpdHkgd2l0aCBhbnkgb2YgdGhlIGNsdXN0ZXJzIGZyb20gdmVudG8gYW5kIHN1cnlhLiAKICAgICsgQm90aCBjbHVzdGVycyBleHByZXNzIE1OREEsIHdoaWNoIGlzIHR5cGljYWxseSBvbmx5IGV4cHJlc3NlZCBpbiBjZWxscyBpbiB0aGUgbW9ub2N5dGUtZ3JhbnVsb2N5dGUgbGluZWFnZS4gCiAgICArIEJvdGggY2x1c3RlcnMgZXhwcmVzcyBMWVosIFMxMDBBOSwgUzEwMEE4LCB3aGljaCBJIHRoaW5rIGFyZSBhbHNvIG1vbm9jeXRlIGdlbmVzLgogICAgKyBUaGVzZSBtaWdodCBiZSBtb25vY3l0ZXMgYnV0IHRoZXkgZG9uJ3QgZXhwcmVzcyBDRDE0LiAKICAgICsgSXQncyBpbiBmYWN0IHBvc3NpYmxlIHRvIGhhdmUgQ0QxNC0tdmUgbW9ub2N5dGUsIGl0IHR1cm5zIG91dCBbQG11a2hlcmplZV9ub24tY2xhc3NpY2FsXzIwMTVdLiBTZWUgaW50cm9kdWN0aW9uIG9mIFtAc2FtcGF0aF9tb25vY3l0ZS1zdWJzZXRzXzIwMThdLCB3aGljaCBzdW1tYXJpemVzIG9ic2VydmF0aW9ucyBmcm9tIFtAdmlsbGFuaV9zaW5nbGUtY2VsbF8yMDE3XS4gVGhlc2UgcGFwZXJzIHBvaW50IHRvIG5vbi1DRDE0IG1vbm9jeXRlIHBvcHVsYXRpb25zLiBJbiBmYWN0IG1vc3QgZ2VuZXMgaW4gdGhlIE1vbm9fMyBjbHVzdGVyIG9mIFtAdmlsbGFuaV9zaW5nbGUtY2VsbF8yMDE3XSBhcmUgZXhwcmVzc2VkIGluIGNsdXN0ZXIgMDMsIGUuZy4gTVhEMSwgVk5OMiwgQ1hDUjIuCiAgICArIE92ZXJhbGwgaXQgbG9va3MgbGlrZSBjbHVzdGVycyAzIGFuZCA4IGFyZSB0d28gdHlwZXMgb2YgbW9ub2N5dGVzLiAoYXQgdGhlIHZlcnkgbGVhc3QgdGhleSBhcmUgZnJvbSB0aGUgbW9ub2N5dGUtZ3JhbnVsb2N5dGUgbGluZWFnZS4pIAogICAgCmBgYHtyfQphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMDEiKV0gPC0gInZpbC5Ib2ZiXzEiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8yMiIpXSA8LSAidmlsLkhvZmJfMiIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzI3IildIDwtICJ2aWwuSG9mYl8zIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMzEiKV0gPC0gInZpbC5Ib2ZiXzQiCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8zNCIpXSA8LSAidmlsLkhvZmJfNSIKCmFubm90YXRpb24kYW5ub3RhdGlvblthbm5vdGF0aW9uJGNsdXN0ZXIgJWluJSBjKCJjbHVzdF8wMyIpXSA8LSAiTW9ub18xIgphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMDgiKV0gPC0gIk1vbm9fMiIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzExIildIDwtICJBUENfMSIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzEyIildIDwtICJBUENfMiIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzI2IildIDwtICJ2aWwuRXJ5Igphbm5vdGF0aW9uJGFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMjgiKV0gPC0gIkdyYW4iCgphbm5vdGF0aW9uJG5vdGVzW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzI3IildIDwtICJhbHNvIGZpYnJvYmxhc3Qgc2lnbmF0dXJlIgphbm5vdGF0aW9uJG5vdGVzW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzM0IildIDwtICJsaWtlbHkgYWxzbyBjb250YWlucyBlcnl0aHJvIgphbm5vdGF0aW9uJG5vdGVzW2Fubm90YXRpb24kY2x1c3RlciAlaW4lIGMoImNsdXN0XzAzIildIDwtICJ1bnN1cmUiCmFubm90YXRpb24kbm90ZXNbYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgYygiY2x1c3RfMDgiKV0gPC0gInVuc3VyZSIKCmFubm90YXRpb25bYW5ub3RhdGlvbiRjbHVzdGVyICVpbiUgcGFzdGUwKCJjbHVzdF8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiMDEiLCAiMDMiLCAiMDgiLCAiMTEiLCAiMTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyMiIsICIyNiIsICIyNyIsICIyOCIsICIzMSIsICIzNCIpKSwgXQpgYGAKCiMjIyBNeWVsb2lkIGNhbmRpZGF0ZSBnZW5lcwpgYGB7ciBmaWcuYXNwPTAuNX0KRG90UGxvdDIoZmVhdHVyZXMgPSBjKCJQVFBSQyIsICMgcGFuCiAgICAgICAgICAgICAgICAgICAgICAiQ0QzMyIsICJDRDE0IiwgIkNENjgiLCAiQ0QxNjMiLCAiQ0xFQzEwQSIsICJITEEtRFJBIiwgIyBhcGMKICAgICAgICAgICAgICAgICAgICAgICJDRUFDQU04IiwgIkNFQUNBTTYiLCAiTVBPIiwgIkVMQU5FIiwgIyBncmFudWxvY3l0ZXMKICAgICAgICAgICAgICAgICAgICAgICJGQ0dSM0EiLCAjIGluZmxhbW1hdG9yeSBtb25vY3l0ZXM/CiAgICAgICAgICAgICAgICAgICAgICAiR1lQQSIgIyBlcnl0aHJvCiAgICAgICAgICAgICAgICAgICAgICApLCAKICAgICAgICAgdGl0bGUgPSAiTXllbG9pZCBjYW5kaWRhdGUgZ2VuZXMiKQpgYGAKCiMjIyBIb2ZiIGNsdXN0ZXJzIG9uIFVNQVAKYGBge3IgZmlnLndpZHRoPTZ9CkRpbVBsb3Qoc2V1ciwgCiAgICAgICAgY29scyA9IGMocmVwKCJHcmV5OTAiLCAzMCksICIjYWVjN2U4IiwgIiNmZjllNGEiLCAiIzJjYTAyYyIsICIjZWQ2NjVkIiwgIiM5NDY3YmQiKSwgCiAgICAgICAgb3JkZXIgPSAocmV2KGMoImNsdXN0XzAxIiwgImNsdXN0XzIyIiwgImNsdXN0XzI3IiwgImNsdXN0XzMxIiwgImNsdXN0XzM0IikpKSkgKyAKICB0aGVtZV9mZXcoKSArCiAgY29vcmRfZml4ZWQoKQpgYGAKIyMjIGNsdXN0XzIyIG1hcmtlcnMgb24gVU1BUApgYGB7ciBmaWcuYXNwPTAuNX0KRGVmYXVsdEFzc2F5KHNldXIpIDwtICJTQ1QiCnAgPC0gRmVhdHVyZVBsb3Qoc2V1ciwgZmVhdHVyZXMgPSBjKCJDRDE2MyIsICJDU0YxUiIsICJDMVFBIiwgIk1SQzEiLCAiQ0QxNCIsICJGT0xSMiIpLCAKICAgICAgICAgICAgICAgICBjZWxscyA9IHJvd25hbWVzKHNldXJAbWV0YS5kYXRhKVtzZXVyQG1ldGEuZGF0YSRzZXVyYXRfY2x1c3RlcnMgPT0gImNsdXN0XzIyIl0sCiAgICAgICAgICAgICAgICAgY29vcmQuZml4ZWQgPSBUUlVFLCBuY29sID0gMywgY29tYmluZSA9IEZBTFNFKSAKCnBbWzFdXSArIHBbWzJdXSArIHBbWzNdXSArIHBbWzRdXSArIHBbWzVdXSArIHBbWzZdXSArIAogIHBsb3RfbGF5b3V0KG5jb2wgPSAzKSArCiAgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gImNsdXN0XzIyIikgJgogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuMjUpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjc1LCAibGluZSIpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgCmBgYAoKIyMjIGNsdXN0XzI3IG1hcmtlcnMgb24gVU1BUApgYGB7cn0KRGVmYXVsdEFzc2F5KHNldXIpIDwtICJTQ1QiCnAgPC0gRmVhdHVyZVBsb3Qoc2V1ciwgZmVhdHVyZXMgPSBjKCJDT0wzQTEiLCAiQ0QxNCIsICJNUkMxIiwgIkxZVkUxIiwgIkNPTDFBMSIpLCBjZWxscyA9IHJvd25hbWVzKHNldXJAbWV0YS5kYXRhKVtzZXVyQG1ldGEuZGF0YSRzZXVyYXRfY2x1c3RlcnMgPT0gImNsdXN0XzI3Il0sCiAgICAgICAgICAgICAgICAgY29vcmQuZml4ZWQgPSBUUlVFLCBuY29sID0gMykgCnBbWzFdXSArIHBbWzJdXSArIHBbWzNdXSArIHBbWzRdXSArIHBbWzVdXSArIAogIHBsb3RfbGF5b3V0KG5jb2wgPSAzKSArCiAgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gImNsdXN0XzI3IikgJgogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuMjUpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjc1LCAibGluZSIpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgCmBgYAoKIyMjIGNsdXN0XzM0IG1hcmtlcnMgb24gVU1BUApgYGB7cn0KRGVmYXVsdEFzc2F5KHNldXIpIDwtICJTQ1QiCnAgPC0gIEZlYXR1cmVQbG90KHNldXIsIAogICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGMoIkdZUEEiLCAiSEJNIiwgIkhCRzIiLCAiSEJHMSIsICJBTEFTMiIsICJBSFNQIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTVJDMSIsICJDRDE2MyIsICJDU0YxUiIpLCAKICAgICAgICAgICAgICAgICAgY2VsbHMgPSByb3duYW1lcyhzZXVyQG1ldGEuZGF0YSlbc2V1ckBtZXRhLmRhdGEkc2V1cmF0X2NsdXN0ZXJzID09ICJjbHVzdF8zNCJdLAogICAgICAgICAgICAgICAgICBjb29yZC5maXhlZCA9IEZBTFNFLCBuY29sID0gMykKcFtbMV1dICsgcFtbMl1dICsgcFtbM11dICsgcFtbNF1dICsgcFtbNV1dICsgcCBbWzZdXSArIHBbWzddXSArIHBbWzhdXSArIHBbWzldXSArIApwbG90X2xheW91dChuY29sID0gMykgKwogIHBsb3RfYW5ub3RhdGlvbih0aXRsZSA9ICJjbHVzdF8zNCIpICYKICB0aGVtZV9idygpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjI1KSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC43NSwgImxpbmUiKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpIApgYGAKCiMjIyBNYXJrZXIgZ2VuZSBwbG90cwpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMDEiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMDMiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMDgiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMTEiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMTIiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMjIiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMjYiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMjciXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMjgiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMzEiXV0KYGBgCgpgYGB7ciBmaWcuYXNwPTF9CnRvcDUwbWFya2VyLnBsb3RzW1siY2x1c3RfMzQiXV0KYGBgCgojIyMgR08gZW5yaWNobWVudApgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzAxIikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzAzIikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzA4IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzExIikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzEyIikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzIyIikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzI2IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzI3IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzI4IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzMxIikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQplbnJpY2hHTzIoImNsdXN0XzM0IikKYGBgCgojIyMgVmVudG8gbWFya2VycwpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJGRNMSwgdGl0bGUgPSAidmVudG8gbWFya2VyczogZE0xIikKYGBgCgpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJGRNMiwgdGl0bGUgPSAidmVudG8gbWFya2VyczogZE0yIikKYGBgCgpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJEhCLCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBIQiIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRNMywgdGl0bGUgPSAidmVudG8gbWFya2VyczogTTMiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMudmVudG8kTU9fMSwgdGl0bGUgPSAidmVudG8gbWFya2VyczogTU9fMSIpCmBgYAoKYGBge3IgZmlnLmFzcD0wLjgsIHdhcm5pbmc9RkFMU0V9CkRvdFBsb3QyKGZlYXR1cmVzID0gbWFya2Vycy52ZW50byRNT18yLCB0aXRsZSA9ICJ2ZW50byBtYXJrZXJzOiBNT18yIikKYGBgCgpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJERDMSwgdGl0bGUgPSAidmVudG8gbWFya2VyczogREMxIikKYGBgCgpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnZlbnRvJERDMiwgdGl0bGUgPSAidmVudG8gbWFya2VyczogREMyIikKYGBgCgojIyMgU3VyeWEgbWFya2VycwpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJ2aWwuSEMiXSwgdGl0bGUgPSAic3VyeWEgbWFya2VyczogdmlsLkhDIikKYGBgCgpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJ2aWwuRUIiXSwgdGl0bGUgPSAic3VyeWEgbWFya2VyczogdmlsLkVCIikKYGBgCgpgYGB7ciBmaWcuYXNwPTAuOCwgd2FybmluZz1GQUxTRX0KRG90UGxvdDIoZmVhdHVyZXMgPSBtYXJrZXJzLnN1cnlhJGdlbmVbbWFya2Vycy5zdXJ5YSRjbHVzdGVyID09ICJkZWMuQVBDIl0sIHRpdGxlID0gInN1cnlhIG1hcmtlcnM6IGRlYy5BUEMiKQpgYGAKCmBgYHtyIGZpZy5hc3A9MC44LCB3YXJuaW5nPUZBTFNFfQpEb3RQbG90MihmZWF0dXJlcyA9IG1hcmtlcnMuc3VyeWEkZ2VuZVttYXJrZXJzLnN1cnlhJGNsdXN0ZXIgPT0gInZpbC5IQyJdLCB0aXRsZSA9ICJzdXJ5YSBtYXJrZXJzOiB2aWwuSEMiKQpgYGAKCiMgRmluYWwgYW5ub3RhdGlvbnMKIyMgTWVyZ2Ugc2ltaWxhciBjbHVzdGVycwpJbiBhZGRpdGlvbiB0byB0aGUgYW5ub3RhdGlvbnMgc28gZmFyLCB3ZSBjYW4gaGF2ZSBhIHNlY29uZCBzZXQgb2YgY29hcnNlci1ncmFpbmVkIGFubm90YXRpb25zIHNvIHRoYXQgd2UgaGF2ZSBmZXdlciBjbHVzdGVycyB0byBkZWFsIHdpdGguIEZvciB0aGlzIHdlIHdpbGwgbWVyZ2Ugc29tZSBzaW1pbGFyIGNsdXN0ZXJzLS0tbW9zdGx5IG5vbi1pbW11bmUgY2x1c3RlcnMsIGxlYXZpbmcgYWxsIGltbXVuZSBjbHVzdGVycyBpbnRhY3QuIAoKYGBge3J9CmFubm90YXRpb24kYW5ub3RhdGlvbl9tZXJnZWQgPC0gYW5ub3RhdGlvbiRhbm5vdGF0aW9uCgphbm5vdGF0aW9uJGFubm90YXRpb25fbWVyZ2VkW2Fubm90YXRpb24kYW5ub3RhdGlvbiAlaW4lIHBhc3RlMCgidmlsLkhvZmJfIiwgYygxOjUpKV0gPC0gInZpbC5Ib2ZiIgphbm5vdGF0aW9uJGFubm90YXRpb25fbWVyZ2VkW2Fubm90YXRpb24kYW5ub3RhdGlvbiAlaW4lIHBhc3RlMCgiZGVjLkRTQ18iLCBjKDE6MikpXSA8LSAiZGVjLkRTQyIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uX21lcmdlZFthbm5vdGF0aW9uJGFubm90YXRpb24gJWluJSBwYXN0ZTAoImRlYy5GQl8iLCBjKDE6MykpXSA8LSAiZGVjLkZCIgphbm5vdGF0aW9uJGFubm90YXRpb25fbWVyZ2VkW2Fubm90YXRpb24kYW5ub3RhdGlvbiAlaW4lIHBhc3RlMCgidmlsLkZCXyIsIGMoMTozKSldIDwtICJ2aWwuRkIiCmFubm90YXRpb24kYW5ub3RhdGlvbl9tZXJnZWRbYW5ub3RhdGlvbiRhbm5vdGF0aW9uICVpbiUgcGFzdGUwKCJBUENfIiwgYygxOjIpKV0gPC0gIkFQQyIKYW5ub3RhdGlvbiRhbm5vdGF0aW9uX21lcmdlZFthbm5vdGF0aW9uJGFubm90YXRpb24gJWluJSBwYXN0ZTAoInZpbC5WQ1RfIiwgYygxOjQpKV0gPC0gInZpbC5WQ1QiCmFubm90YXRpb24kYW5ub3RhdGlvbl9tZXJnZWRbYW5ub3RhdGlvbiRhbm5vdGF0aW9uICVpbiUgcGFzdGUwKCJ2aWwuU0NUXyIsIGMoMToyKSldIDwtICJ2aWwuU0NUIgphbm5vdGF0aW9uJGFubm90YXRpb25fbWVyZ2VkW2Fubm90YXRpb24kYW5ub3RhdGlvbiAlaW4lIGMoImRlYy5FbmRvIiwgImRlYy5FbmRvLkwiKV0gPC0gImRlYy5FbmRvIgpgYGAKCmBgYHtyfQojIHJlYXJyYW5nZSBjb2x1bW5zIGFuZCB3cml0ZSBhbm5vdGF0aW9ucyB0byBjc3YKYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uWywgYygiY2x1c3RlciIsICJhbm5vdGF0aW9uIiwgImFubm90YXRpb25fbWVyZ2VkIiwgIm5vdGVzIildCgp3cml0ZS5jc3YoYW5ub3RhdGlvbiwgIi4uL3Jlc3VsdHMvMDJfYW5ub3RhdGlvbi9maWxlcy9hbm5vdGF0aW9ucy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgoKYGBge3J9CmFubm90YXRpb24gJT4lIGthYmxlKGNhcHRpb24gPSAiRmluYWwgYW5ub3RhdGlvbnMgb2YgYWxsIGNsdXN0ZXJzIikgJT4lIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKIyMgQWRkIGFubm90YXRpb25zIHRvIGBTZXVyYXRgIG9iamVjdApgYGB7cn0KIyBhZGQgYW5ub3RhdGlvbnMKc2V1ckBtZXRhLmRhdGEkYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uJGFubm90YXRpb25bbWF0Y2goCiAgeCA9IHNldXJAbWV0YS5kYXRhJHNldXJhdF9jbHVzdGVycywKICB0YWJsZSA9IGFubm90YXRpb24kY2x1c3RlcikKICBdCgpzZXVyQG1ldGEuZGF0YSRhbm5vdGF0aW9uX21lcmdlZCA8LSBhbm5vdGF0aW9uJGFubm90YXRpb25fbWVyZ2VkW21hdGNoKAogIHggPSBzZXVyQG1ldGEuZGF0YSRzZXVyYXRfY2x1c3RlcnMsIAogIHRhYmxlID0gYW5ub3RhdGlvbiRjbHVzdGVyKQogIF0KCmBgYAoKCmBgYHtyfQojIHdyaXRlIHNldXJhdCBvYmplY3QKc2F2ZVJEUyhzZXVyLCBmaWxlID0gIi4uL2RhdGEvc2V1cmF0LW9iamVjdF9hbm5vdGF0ZWQucmRzIikKYGBgCgojIyBQbG90cwojIyMgVU1BUDogYWxsIGFubm90YXRpb25zCmBgYHtyLCBmaWcuYXNwPTEsIGZpZy53aWR0aD03fQpJZGVudHMoc2V1cikgPC0gc2V1ckBtZXRhLmRhdGEkYW5ub3RhdGlvbgpEaW1QbG90KHNldXIsIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gNCwgc2h1ZmZsZSA9IFRSVUUpICsKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQoKZ2dzYXZlKGxhc3RfcGxvdCgpLCBmaWxlbmFtZSA9ICIuLi9yZXN1bHRzLzAyX2Fubm90YXRpb24vcGxvdHMvdW1hcF9hbm5vdGF0ZWQucGRmIiwgCiAgICAgICBkZXZpY2UgPSAicGRmIiwgd2lkdGggPSA3LCBoZWlnaHQgPSA3LCB1bml0cyA9ICJpbiIpCmBgYAojIyMgVU1BUDogbWVyZ2VkIGFubm90YXRpb25zCmBgYHtyLCBmaWcuYXNwPTEsIGZpZy53aWR0aD03fQpJZGVudHMoc2V1cikgPC0gc2V1ckBtZXRhLmRhdGEkYW5ub3RhdGlvbl9tZXJnZWQKRGltUGxvdChzZXVyLCBsYWJlbCA9IFRSVUUsIHJlcGVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDQsIHNodWZmbGUgPSBUUlVFKSArCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkKCmdnc2F2ZShsYXN0X3Bsb3QoKSwgZmlsZW5hbWUgPSAiLi4vcmVzdWx0cy8wMl9hbm5vdGF0aW9uL3Bsb3RzL3VtYXBfYW5ub3RhdGVkX21lcmdlZC5wZGYiLCAKICAgICAgIGRldmljZSA9ICJwZGYiLCB3aWR0aCA9IDcsIGhlaWdodCA9IDcsIHVuaXRzID0gImluIikKYGBgCgojIFNlc3Npb24gSW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCiMgUmVmZXJlbmNlcwoK